Back to skill

Security audit

ecovacs-skills-pet-control

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent pet-robot control skill, but it handles a powerful access key and physical/privacy-sensitive device controls with some overbroad and under-protected paths.

Review before installing. Use only official HTTPS Ecovacs gateways, avoid setting ECOVACS_PORTAL_URL unless you trust the endpoint, prefer ECOVACS_AK over set-ak storage, protect or delete ~/.ecovacs_session.json if used, and require clear user intent before enabling camera/mic, moving the robot, playing repeated sounds, resetting, or scheduling routines.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/ecovacs.py:58
Finding
Unrestricted Gateway Override Can Disclose the Access Key<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ecovacs.py`, lines 58–114 **Vulnerability Type**: Unvalidated sensitive-data transmission destination **Risk Level**: High ### Vulnerable Code ```python def portal_base(): return os.environ.get("ECOVACS_PORTAL_URL", "https://open.ecovacs.cn").rstrip("/") def http_get_json(url): req = urlreq.Request(url, headers={"Accept": "application/json"}) with urlreq.urlopen(req, timeout=45) as resp: return json.loads(resp.read().decode("utf-8")) def http_post_json(url, body): data = json.dumps(body).encode("utf-8") req = urlreq.Request( url, data=data, headers={"Content-Type": "application/json", "Accept": "application/json"}, ) with urlreq.urlopen(req, timeout=45) as resp: return json.loads(resp.read().decode("utf-8")) def skill_device_list(ak): q = urlparse.quote(ak, safe="") url = f"{portal_base()}/robot/skill/deviceList?ak={q}" return http_get_json(url) def skill_pet_cmd(ak, nick_name, cmd, body_data=None): """宠物控机请求。""" url = f"{portal_base()}/robot/skill/pet/cmd" body = {"ak": ak, "nickName": nick_name, "cmd": cmd} if body_data is not None: body["data"] = body_data return http_post_json(url, body) ``` ### Technical Analysis The `ECOVACS_PORTAL_URL` environment variable completely controls the destination to which the script sends the Ecovacs Open Platform Access Key. The value is not validated for an approved hostname or an HTTPS scheme. For device discovery, the AK is included in the URL query string. Query parameters can be recorded by HTTP servers, reverse proxies, observability systems, and URL logs. For control requests, the AK is transmitted in the JSON body together with the device nickname, command name, and command data. Sending the AK to the official Ecovacs gateway is necessary for the declared device-control functionality. However, permitting any environment-selected host, ...[truncated 1479 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Allow only the documented HTTPS gateway origins by default: - `https://open.ecovacs.cn` - `https://open.ecovacs.com` 2. Parse the configured URL and reject: - Schemes other than `https`. - Embedded credentials. - Unexpected ports. - Hosts outside an administrator-controlled allowlist. - Ambiguous or malformed hostnames. 3. If private gateways are a legitimate requirement, require explicit administrative opt-in rather than trusting an unrestricted environment variable. 4. Change device discovery to use the supported POST form with `{"ak": "<AK>"}` so the credential is not placed in the URL query string. 5. Avoid logging complete request URLs, request bodies, or responses that may contain authentication material. 6. Document the trust boundary and warn users that custom gateways receive the AK and device command data. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ecovacs.py:80
Finding
Access Key Stored Without Explicit Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ecovacs.py`, lines 80–84 **Vulnerability Type**: Insecure plaintext credential storage **Risk Level**: Medium ### Vulnerable Code ```python def save_ak(ak): path = SESSION_FILE data = {"ak": ak.strip()} with open(path, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) print(f"✅ 已保存 AK 到 {path}") ``` The destination is defined earlier as: ```python SESSION_FILE = os.path.expanduser("~/.ecovacs_session.json") ``` ### Technical Analysis The `set-ak` command writes a long-lived access key to `~/.ecovacs_session.json` as plaintext. The file is opened without explicitly setting restrictive permissions, so its resulting mode depends on the process umask and any permissions already assigned to an existing file. On systems with a permissive umask, shared home-directory access, or a previously created file with broad permissions, another local account or process may be able to read the credential. Reopening an existing file does not automatically tighten its permissions. Plaintext storage may be unavoidable when no credential manager is available, but a file containing an access key should be created atomically with owner-only access and should not rely solely on ambient process configuration. ### Attack Path 1. The user runs `python3 scripts/ecovacs.py set-ak <ak>` in an environment with permissive file permissions or an existing broadly readable session file. 2. The script writes the AK to `~/.ecovacs_session.json` without enforcing mode `0600`. 3. Another local user or process with filesystem access reads the file. 4. The attacker extracts the AK. 5. The attacker reuses the AK against the Ecovacs gateway, subject to the key's validity and server-side authorization. ### Impact Assessment Exploitation requires local filesystem access or another process running with access to the user's home directory. A disclosed AK may allow the attacker to enu ...[truncated 273 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer the `ECOVACS_AK` environment variable or an operating-system credential store when persistent storage is unnecessary. 2. If file storage is required, create the file atomically with owner-only mode `0600`, for example by using `os.open` with `O_CREAT | O_WRONLY | O_TRUNC` and mode `0o600`. 3. Apply `os.chmod(path, 0o600)` to existing regular files before or immediately after writing. 4. Reject symbolic links and verify that the destination is a regular file owned by the current user. 5. Write through a securely created temporary file in the same directory, flush it, and atomically replace the destination. 6. Never print or log the AK value, and provide guidance for rotating the AK if local disclosure is suspected. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (17)

Tainted flow: 'req' from os.environ.get (line 96, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
def http_get_json(url):
    req = urlreq.Request(url, headers={"Accept": "application/json"})
    with urlreq.urlopen(req, timeout=45) as resp:
        return json.loads(resp.read().decode("utf-8"))
Confidence
91% confidence
Finding
The request destination is derived from ECOVACS_PORTAL_URL via portal_base(), and that environment-controlled value is used to build URLs passed into urlopen without any allowlist or host validation. If an attacker can influence the environment or execution context, they can redirect requests containing the user's access key and device commands to an arbitrary server, causing credential exfiltration and unauthorized robot control.

Tainted flow: 'req' from os.environ.get (line 96, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
data=data,
        headers={"Content-Type": "application/json", "Accept": "application/json"},
    )
    with urlreq.urlopen(req, timeout=45) as resp:
        return json.loads(resp.read().decode("utf-8"))
Confidence
96% confidence
Finding
POST requests send sensitive material directly to the environment-controlled portal endpoint, including the AK in the JSON body and robot control payloads. A malicious or compromised ECOVACS_PORTAL_URL can therefore receive the credential and all issued commands, enabling full misuse of the linked pet robot account/device integration.

Missing User Warnings

High
Confidence
97% confidence
Finding
The documented automatic wake-up behavior explicitly enables the camera and changes work mode before sending display/motion actions, but it does not present this as a prominent privacy/security warning requiring consent. That means a routine action can silently transition the device into a more privacy-sensitive state and alter its operating mode, surprising users and potentially exposing camera functionality without informed approval.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README advertises capabilities that can change privacy- and integrity-sensitive device settings and behavior, including microphone/camera toggles, wake word/nickname changes, sound/motion execution, and scheduled routines, but it does not prominently warn that these actions can affect surveillance, device behavior, or nearby people/pets. In an agent skill context, normalizing these controls without explicit consent guidance increases the risk of unauthorized or socially engineered use, especially because the skill is designed for natural-language execution.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The README advertises microphone/camera changes and physical robot actions without clearly warning users about privacy implications or real-world side effects. In a voice/agent skill context, commands that can enable sensing or trigger device movement are safety-relevant because users may not realize they are authorizing surveillance-affecting changes or physical behavior.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill documents and encourages use of environment variables, network requests, and script execution, but it does not declare any explicit tool scope or permission boundaries. In an agent ecosystem, this can lead to overbroad execution authority, making it easier for an agent to access secrets, write files, or perform networked control actions beyond what a user expects.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
This skill controls a physical robot, including movement, display changes, audio, and settings, yet the introductory usage flow lacks a clear safety warning that commands can trigger real-world motion and state changes. Users or downstream agents may invoke actions without appreciating the physical, privacy, or nuisance risks to nearby people, pets, or property.

External Transmission

Medium
Category
Data Exfiltration
Content
**Device list**

```bash
curl -sS "${BASE_URL}/robot/skill/deviceList?ak=YOUR_AK"
```

**Pet control**
Confidence
84% confidence
Finding
The skill instructs users to send the Access Key in a query string for the device list endpoint and in HTTP requests to external Ecovacs gateways. Transmitting credentials to external services is expected for this integration, but placing secrets in URLs is riskier because query strings are commonly logged by shells, proxies, browser history, and monitoring systems.

External Transmission

Medium
Category
Data Exfiltration
Content
### Device list

```bash
curl -sS "${BASE_URL}/robot/skill/deviceList?ak=YOUR_AK"
```

`POST` with body `{"ak":"<AK>"}` is also supported. On success, `data[]` lists devices; **`product_category: FAMIBOT`** identifies pet robots.
Confidence
96% confidence
Finding
The example sends the Access Key in a GET query string, which is commonly recorded in browser history, proxy logs, server access logs, monitoring tools, and referrer-like telemetry. Exposure of the AK could permit unauthorized API use, including device discovery and potentially follow-on control actions against FAMIBOT devices.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This markdown file describes potentially disruptive operations such as putting the device to sleep and resetting it, including cancellation of already queued timed actions. The surrounding documentation provides usage details and examples but does not warn users that these commands can interrupt ongoing behavior or clear scheduled actions.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This code exposes user-facing natural language in Chinese, including the routine label and the error message, and several aliases are Chinese-only. It does not offer a language choice or document that the skill is intentionally limited to a Chinese-speaking locale, which violates the language/locale policy criteria.

Natural-Language Policy Violations

Low
Confidence
79% confidence
Finding
The file states 'Default: English' while offering a Chinese alternative, which imposes a language default rather than presenting language selection neutrally. SQP-3 flags language or locale policy violations when a specific language is forced without explicit user opt-in.

Natural-Language Policy Violations

Low
Confidence
65% confidence
Finding
The file presents the skill documentation entirely in Chinese and includes region-specific setup guidance, but it does not clearly state that language/locale is user-selectable when interacting with the skill. Because SQP-3 covers natural-language policy issues around forced language or locale, this may indicate a language constraint without explicit opt-in.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The documentation instructs users to place the Access Key directly in requests but does not warn that the AK is a sensitive credential. This increases the chance the key will be copied into shell history, screenshots, logs, or shared examples, which could allow unauthorized control of the user's robot devices if exposed.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
The entire skill file is written in Chinese and provides no indication that other languages are supported or that the user can choose their preferred language. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy concern.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
A language or locale policy issue can arise when a skill forces a specific language without offering user choice. This file presents all instructions and examples exclusively in Chinese, with no indication that the language is optional, configurable, or intentionally limited to a specific audience.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
This code file contains extensive natural-language instructions and user-facing text exclusively in Chinese, including the module docstring and command help. Under the policy rule for language/locale, forcing a specific language without user opt-in is a natural-language policy concern unless the locale constraint is explicitly justified.

Static analysis

No suspicious patterns detected.