T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/imou_client.py:23
- Finding
- Unrestricted API destination permits sensitive data disclosure<![CDATA[ ## Vulnerability Details **File Location**: `scripts/imou_client.py:23-64`; supporting configuration in `scripts/multimodal_analysis.py:36-40` **Vulnerability Type**: User-controlled network destination without transport or hostname validation **Risk Level**: High ### Vulnerable Code ```python def _get_base_url(): return os.environ.get("IMOU_BASE_URL", "").strip() or DEFAULT_BASE_URL def _build_sign(time_sec: int, nonce: str, app_secret: str) -> str: """Build sign: MD5 of 'time:{time},nonce:{nonce},appSecret:{app_secret}' (UTF-8), 32-char lowercase hex.""" raw = f"time:{time_sec},nonce:{nonce},appSecret:{app_secret}" return hashlib.md5(raw.encode("utf-8")).hexdigest() def _request(method: str, params: dict, app_id: str, app_secret: str, base_url: str = None) -> dict: """ Send one Open API request. :param method: API method name (e.g. 'accessToken', 'humanDetect'). :param params: Request params object. :param app_id: App ID. :param app_secret: App secret for sign. :param base_url: Optional base URL; uses env IMOU_BASE_URL or default if None. :return: Full response body as dict; check result.code for '0'. """ base = base_url or _get_base_url() url = f"{base.rstrip('/')}/openapi/{method}" time_sec = int(time.time()) nonce = uuid.uuid4().hex sign = _build_sign(time_sec, nonce, app_secret) body = { "system": { "ver": "1.0", "appId": app_id, "sign": sign, "time": time_sec, "nonce": nonce, }, "id": str(uuid.uuid4()), "params": params, } headers = { "Content-Type": "application/json", OPENCLAW_HEADER: OPENCLAW_HEADER_VALUE, } resp = requests.post(url, headers=headers, json=body, timeout=60) resp.raise_for_status() return resp.json() ``` The CLI passes the environment-controlled destination directly into the client: ```python APP_ID = os.environ.ge ...[truncated 3071 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse the destination with `urllib.parse.urlsplit()` rather than concatenating an unchecked string. 2. Require the `https` scheme and reject plaintext HTTP. 3. Allowlist the documented Imou hosts: - `openapi.lechange.cn` - `openapi-sg.easy4ip.com` - `openapi-fk.easy4ip.com` - `openapi-or.easy4ip.com` 4. Permit only expected HTTPS ports, such as 443, and reject user-information components, fragments, IP literals, and unexpected base paths. 5. Set `allow_redirects=False` for sensitive POST requests. If redirects are operationally necessary, validate every redirect destination against the same allowlist before resending data. 6. Reject empty application credentials and validate token responses before using them. 7. If private or testing endpoints must be supported, require a separate explicit unsafe opt-in flag and present a warning that credentials and image data will be sent to a non-Imou server. 8. Correct `SKILL.md` so its requirement for an explicitly configured base URL is consistent with the implementation’s default behavior. 9. Document that image and biometric data must only be submitted with appropriate authorization and consent. ]]>
