T09 · Insecure Skill Coding Practices
- Location
scripts/_send_lib.py:31- Finding
WeCom Webhook Secret Exposed Through Subprocess Command-Line Arguments
- Content
View full analysis
str: """Read webhook URL from environment; raise RuntimeError if absent.""" key = os.environ.get("WECOM_WEBHOOK_KEY", "") if not key: raise RuntimeError("WECOM_WEBHOOK_KEY environment variable not set") return f"https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={key.strip()}" def wx(text: str, max_retries: int = 2) -> int: """Send a WeCom message with automatic retries.""" payload = json.dumps( {"msgtype": "text", "text": {"content": text}}, ensure_ascii=False, ) for attempt in range(max_retries + 1): try: url = get_webhook_url() except RuntimeError as _e: print(f"[{_ts()}] [WX] webhook URL retrieval failed: {_e}") return -1 r = subprocess.run( ["curl", "-s", "-X", "POST", url, "-H", "Content-Type: application/json", "-d", "@-"], input=payload.encode("utf-8"), capture_output=True, ) ``` The same vulnerable pattern is independently duplicated in `scripts/send_evening_report.py:100-115`. ### Technical Analysis The WeCom webhook key is embedded in a URL and passed directly to `curl` as a command-line argument. While the report body is correctly passed through standard input, the complete webhook URL—including the credential in its `key` query parameter—appears in the child process's argument vector. While `curl` is running, the URL may be observable through process inspection mechanisms such as: - `/proc//cmdline` - Process-monitoring utilities - System telemetry or command-line audit logs - Container or host-level monitoring agents Access depends on the host's process-visibility configuration, user separation, and monitoring permissions. Nevertheless ...[truncated 1620 chars]- Remediation
View remediation
int: try: url = get_webhook_url() except RuntimeError as exc: print(f"[WX] Webhook URL retrieval failed: {exc}") return -1 payload = {"msgtype": "text", "text": {"content": text}} for attempt in range(max_retries + 1): try: response = requests.post(url, json=payload, timeout=10) response.raise_for_status() if response.json().get("errcode") == 0: return 0 except (requests.RequestException, ValueError) as exc: print(f"[WX] Request failed: {type(exc).__name__}") return -1 ``` 2. Refactor `scripts/send_evening_report.py` to use the hardened shared helper instead of maintaining a duplicate implementation. 3. Ensure exceptions and logs never print the complete webhook URL. 4. Restrict environment-variable and process visibility at the host or container boundary as defense in depth. 5. Rotate the existing WeCom webhook key if there is any indication that command-line monitoring or process telemetry has retained it. ]]>
