T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/yuque_cli.py:214
- Finding
- Yuque API Token Can Be Transmitted to an Attacker-Controlled or Plaintext Endpoint## Vulnerability Details **File Location**: `scripts/yuque_cli.py:214-221`, `scripts/yuque_cli.py:232-236`, and `scripts/yuque_cli.py:77-96` **Vulnerability Type**: Insufficient endpoint validation leading to credential disclosure **Risk Level**: High ### Vulnerable Code ```python parsed = urlparse(url) if not parsed.scheme: parsed = urlparse("https://" + url) if not re.search(r'yuque\.com$', parsed.hostname or ""): return None, "URL must be a yuque.com domain (e.g. https://xxx.yuque.com/group/book)." base_url = f"{parsed.scheme}://{parsed.hostname}" ``` The resulting endpoint is then used to transmit the token: ```python def verify_connection(token, base_url, group_login, book_slug): """Test the token and repo by calling the list docs API. Returns (success, message).""" url = f"{base_url}/api/v2/repos/{group_login}/{book_slug}/docs?limit=1" try: resp = requests.get( url, headers={"X-Auth-Token": token, "User-Agent": "yuque-cli/1.0"}, timeout=15, ) ``` Normal API requests also attach the token to the configured base URL: ```python self.session.headers.update({ "X-Auth-Token": token, "Content-Type": "application/json", "User-Agent": "yuque-cli/1.0", }) def _request(self, method, path, **kwargs): url = self.base_url + path resp = self.session.request(method, url, **kwargs) ``` ### Technical Analysis The hostname check only tests whether the hostname text ends with `yuque.com`. It does not require a DNS label boundary. A domain such as `attackeryuque.com` therefore passes validation even though it is not controlled by Yuque. The parser also preserves the user-supplied scheme without requiring HTTPS. A URL beginning with `http://` can consequently cause the API token to be transmitted in plaintext. In addition, `requests` follows redirects by default. The implementation does not disabl ...[truncated 1619 chars]
- Remediation
- ## Remediation Suggestions 1. Require HTTPS explicitly: ```python if parsed.scheme.lower() != "https": return None, "Yuque URLs must use HTTPS." ``` 2. Validate DNS label boundaries: ```python hostname = (parsed.hostname or "").lower().rstrip(".") if hostname != "yuque.com" and not hostname.endswith(".yuque.com"): return None, "URL must use yuque.com or a yuque.com subdomain." ``` 3. Reject embedded credentials, unexpected ports, fragments, and malformed hostnames. 4. Apply the same validation to `YUQUE_BASE_URL` loaded from environment variables. Validation only during setup is insufficient because `.env` can be edited independently. 5. Disable automatic redirects for token-bearing requests or manually follow redirects only after validating every destination: ```python resp = requests.get(url, headers=headers, timeout=15, allow_redirects=False) ``` 6. Ensure that the token is never forwarded when the scheme, hostname, or port changes. 7. Add regression tests covering `attackeryuque.com`, `yuque.com.attacker.example`, plaintext HTTP, embedded credentials, trailing-dot hostnames, unexpected ports, and cross-origin redirects.
