Back to skill

Security audit

smarthome

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real smart-home controller, but it needs review because it can operate physical devices and handles long-lived smart-home credentials with weak safeguards.

Review this before installing if the account tokens can control important devices such as locks, heaters, outlets, cameras, or appliances. Use a narrowly scoped Home Assistant account, restrict the config file to owner-only access, prefer trusted local/official HTTPS endpoints, rotate credentials if endpoint settings were ever untrusted, and avoid broad fuzzy commands until the skill adds exact matching, confirmation, and platform-specific routing.

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/smart.py:25
Finding
Authentication Credentials Can Be Transmitted to Untrusted Configured Endpoints<![CDATA[ ## Vulnerability Details **File Location**: `scripts/smart.py`, lines 25-63 **Vulnerability Type**: Unrestricted transmission of authentication material to user-configurable endpoints **Risk Level**: High ### Vulnerable Code ```python def ha_control(config, entity_id, action): ha = config.get("homeassistant") if not ha or '.' not in entity_id: return False domain = entity_id.split('.')[0] svc = "turn_on" if action == "on" else "turn_off" url = f"{ha['url']}/api/services/{domain}/{svc}" headers = {"Authorization": f"Bearer {ha['token']}"} try: r = requests.post(url, headers=headers, json={"entity_id": entity_id}, timeout=5) return r.status_code == 200 except: return False # --- Engine B: Tuya Smart --- def tuya_request(t_conf, method, path, body=None): import time aid, secret = t_conf['access_id'], t_conf['access_secret'] endpoint = t_conf['endpoint'] t = str(int(time.time() * 1000)) def calc_sign(msg): return hmac.new(secret.encode(), msg.encode(), hashlib.sha256).hexdigest().upper() r_tk = requests.get(f"{endpoint}/v1.0/token?grant_type=1", headers={"client_id": aid, "sign": calc_sign(aid + t), "t": t, "sign_method": "HMAC-SHA256"}) token = r_tk.json().get("result", {}).get("access_token") t = str(int(time.time() * 1000)) body_hash = hashlib.sha256((json.dumps(body) if body else "").encode()).hexdigest() string_to_sign = f"{method}\n{body_hash}\n\n{path}" sign = hmac.new(secret.encode(), (aid + token + t + string_to_sign).encode(), hashlib.sha256).hexdigest().upper() headers = {"client_id": aid, "access_token": token, "sign": sign, "t": t, "sign_method": "HMAC-SHA256", "Content-Type": "application/json"} res = requests.request(method, endpoint + path, headers=headers, json=body) return res.json().get("success", False) ``` ### Technical Analysis Both Home Assistant and Tuya destinations are read directly fr ...[truncated 2599 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require HTTPS for every Tuya API endpoint and reject cleartext HTTP. 2. Allowlist the documented official Tuya regional API hostnames instead of accepting an arbitrary host. 3. Parse URLs with a standard URL parser and reject embedded credentials, fragments, unexpected schemes, and unexpected ports. 4. Restrict Home Assistant endpoints to local or explicitly trusted hosts by default. Require a clear opt-in and warning before sending a token to a public address. 5. Warn or fail closed when a Home Assistant URL uses HTTP outside loopback or a trusted private network. Prefer HTTPS even on private networks. 6. Store configuration with owner-only permissions, such as mode `0600`, and verify permissions before loading credentials. 7. Use narrowly scoped service accounts and rotate any credentials that may have been transmitted to an untrusted endpoint. 8. Add connection and read timeouts to every request, including both Tuya requests. 9. Validate TLS certificates and do not introduce a certificate-verification bypass. 10. Update the documentation to accurately explain which user-configured destinations may receive credentials and smart-home metadata. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/smart.py:13
Finding
Ambiguous Device Resolution and Unconditional Cross-Platform Fallback Can Operate Unintended Devices<![CDATA[ ## Vulnerability Details **File Location**: `scripts/smart.py`, lines 13-22 and 75-94 **Vulnerability Type**: Unsafe device selection, action validation, and cross-platform fallback **Risk Level**: Medium ### Vulnerable Code ```python def find_device_id(target_name): if not CACHE_FILE.exists(): return target_name, None devices = json.loads(CACHE_FILE.read_text()) # Prefer matching names, then IDs for d in devices: if target_name in d['name'] or target_name == d['id']: return d['id'], d.get('platform') return target_name, None ``` ```python elif cmd == "control": name = sys.argv[2] action = sys.argv[3] # Automatically resolve the device name to an ID real_id, platform = find_device_id(name) print(f"🔄 Processing: {name} ({real_id})...") # Try Home Assistant if ha_control(conf, real_id, action): print("✅ [HA] Control successful") else: # If HA fails or it is not an HA device, try Tuya print("⚠️ [HA] Failed, switching to Tuya Cloud...") body = {"commands": [{"code": "switch_1", "value": (action=="on")}]} if tuya_request(conf["tuya"], "POST", f"/v1.0/iot-03/devices/{real_id}/commands", body): print("✅ [Tuya] Fallback successful") else: print("❌ [Fatal] All platforms failed; check network or configuration") ``` The action is also converted without strict validation: ```python svc = "turn_on" if action == "on" else "turn_off" ``` ### Technical Analysis The lookup function accepts a substring match and returns the first matching cache entry. It does not determine whether several devices match, prioritize exact names before partial names, or request confirmation for an ambiguous result. Cache ordering therefore influences which physical device is selected. Although `find_device_id` returns the cached platform, the control flow does not use ...[truncated 2193 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate CLI argument counts before indexing `sys.argv`. 2. Restrict actions to an explicit allowlist such as `{"on", "off"}` and reject every other value with no network request. 3. Resolve exact device-name matches first, then exact ID matches, and only then perform optional fuzzy matching. 4. If more than one fuzzy match exists, display the candidates and require explicit user selection or confirmation. 5. Treat an unresolved name as an error rather than using arbitrary user input as a device ID. 6. Honor the cached `platform` field and route requests only to the identified platform. 7. Do not use a failure on one platform as authorization to command another platform. 8. Permit cross-platform fallback only when the cache contains an explicit, verified mapping between the same physical device's Home Assistant and Tuya identifiers. 9. Distinguish connection, authentication, validation, and not-found failures instead of collapsing every failure into `False`. 10. Protect `device_cache.json` with owner-only permissions and validate its schema before trusting IDs, names, and platform values. 11. Require confirmation for security-sensitive or safety-sensitive device classes before issuing physical control commands. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (17)

Credential Access

High
Category
Privilege Escalation
Content
## Getting Home Assistant Token
1. Open Home Assistant → Profile (bottom left)
2. Scroll to "Long-Lived Access Tokens"
3. Click "Create Token" and name it (e.g., "Clawdbot")
4. Copy the token immediately (it is only displayed once)
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## Getting Home Assistant Token
1. Open Home Assistant → Profile (bottom left)
2. Scroll to "Long-Lived Access Tokens"
3. Click "Create Token" and name it (e.g., "Clawdbot")
4. Copy the token immediately (it is only displayed once)
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
Solution: Check the HA_URL and ensure Home Assistant is running and accessible.

### Error: 401 Unauthorized (HA)
Solution: The token has expired or is invalid. Generate a new Long-Lived Access Token.

### Device Not Responding
Solutions:
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
Solution: Check the HA_URL and ensure Home Assistant is running and accessible.

### Error: 401 Unauthorized (HA)
Solution: The token has expired or is invalid. Generate a new Long-Lived Access Token.

### Device Not Responding
Solutions:
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Session Persistence

Medium
Category
Rogue Agent
Content
设置
Option 1: Config File (Recommended)
选项1:配置文件(推荐)
Create ~/.config/universal-smarthome/config.json:
创建~/.config/universal-smarthome/config.json:

{
Confidence
84% confidence
Finding
The duplicated finding points to the same behavior: persistent storage of access tokens and API secrets in a local config file. Long-lived secrets stored unencrypted increase the blast radius of host compromise and make accidental disclosure through backups, screenshots, or file sharing more likely.

Session Persistence

Medium
Category
Rogue Agent
Content
设置
Option 1: Config File (Recommended)
选项1:配置文件(推荐)
Create ~/.config/universal-smarthome/config.json:
创建~/.config/universal-smarthome/config.json:

{
Confidence
84% confidence
Finding
The duplicated finding points to the same behavior: persistent storage of access tokens and API secrets in a local config file. Long-lived secrets stored unencrypted increase the blast radius of host compromise and make accidental disclosure through backups, screenshots, or file sharing more likely.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill explicitly supports fuzzy device-name matching for power-control actions, but the documentation does not warn users that ambiguous requests like "打开灯" may affect the wrong device. In a smart-home context, unintended control of lights, appliances, or other connected devices can create safety, privacy, or property risks even without malicious code.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The script is explicitly capable of controlling smart-home devices through both Home Assistant and Tuya cloud APIs, which creates real-world impact beyond ordinary data processing. In the provided context there is no authentication, authorization, confirmation, or scope restriction around dangerous actions, so anyone able to invoke the script with valid config can trigger device state changes across local and cloud-connected systems.

External Transmission

Medium
Category
Data Exfiltration
Content
headers = {"Authorization": f"Bearer {ha['token']}"}

    try:
        r = requests.post(url, headers=headers, json={"entity_id": entity_id}, timeout=5)
        return r.status_code == 200
    except: return False
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Tainted flow: 'headers' from requests.get (line 61, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
headers = {"Authorization": f"Bearer {ha['token']}"}

    try:
        r = requests.post(url, headers=headers, json={"entity_id": entity_id}, timeout=5)
        return r.status_code == 200
    except: return False
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Missing User Warnings

Medium
Confidence
85% confidence
Finding
The Tuya control path sends device commands to a third-party cloud service without any user-facing warning, approval step, or transparency about external transmission. In a smart-home context, undisclosed cloud control is security-relevant because it can affect physical devices and expose operational metadata to an external platform.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
Several printed status and error messages are only presented in Chinese, with no option for users to select another language or locale. This is a natural-language policy concern because it forces a specific language without documented justification or opt-in.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The documentation describes direct control of real smart-home appliances but does not prominently warn about the physical-world consequences of turning devices on or off. In a home-automation context, ambiguous or mistaken commands can affect locks, heaters, ovens, lights, or other devices, creating safety, privacy, or property risks.

Session Persistence

Medium
Category
Rogue Agent
Content
## Setup
Option 1: Config File (Recommended)
Create ~/.config/universal-smarthome/config.json:

```json
{
Confidence
81% confidence
Finding
The recommended setup stores Home Assistant and Tuya credentials persistently in a local config file under the user's home directory. While common, persistent plaintext credential storage increases exposure if the host is compromised, backups are leaked, file permissions are weak, or other local users can read the file.

Session Persistence

Medium
Category
Rogue Agent
Content
## Common Issues & Solutions
### Error: Missing Configuration File
Solution: Create the file ~/.config/universal-smarthome/config.json

### Error: 1004: Invalid Signature
Solution: Check your Tuya access_id and access_secret. Ensure the endpoint matches your region (cn/com/us/eu).
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Natural-Language Policy Violations

Low
Confidence
80% confidence
Finding
The file presents the skill name and operational examples primarily in Chinese, including user utterances such as "打开客厅灯" and "打开灯", but it does not state that the skill is region-specific or provide language-choice guidance. Under the policy, forcing a specific language without opt-in can be a natural-language policy issue.

Intent-Code Divergence

Low
Confidence
95% confidence
Finding
The inline comment says the real implementation would fetch devices from Home Assistant and Tuya and write them into CACHE_FILE, but the actual branch merely prints a success message. This is more than an omission because the user-visible output asserts synchronization happened when no such action occurs in this file.

Static analysis

No suspicious patterns detected.