T09 · Insecure Skill Coding Practices
- Location
scripts/base.py:17- Finding
Unvalidated API Base URL Allows Signed Requests to Arbitrary Destinations
- Content
View full analysis
dict: """ Generate API request headers. """ timestamp = str(int(time.time() * 1000)) nonce = str(random.randint(0, 999999)) signature = encrypt(f"{nonce};{KEY};{timestamp};{UID};") return { 'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Uid': UID, 'X-Nonce': nonce, 'X-Timestamp': timestamp, 'X-Signature': signature } ``` ```python def call_api(endpoint: str, params: dict = None, method: str = 'GET') -> dict: """ Unified API request function. """ url = BASE_URL + endpoint headers = get_headers() params = params or {} try: if method.upper() == 'POST': response = requests.post(url, headers=headers, json=params, timeout=30) else: response = requests.get(url, headers=headers, params=params, timeout=30) return response.json() ``` ### Technical Analysis The API destination is read directly from `CHINADAAS_BASE_URL` and concatenated with endpoint paths without parsing or validating the scheme, hostname, port, or resolved address. Although the Skill documentation states that HTTPS requests are sent to the Qibook service, the implementation does not enforce HTTPS or restrict requests to `qibook.com`. Every request includes the account UID and a request-specific authentication signature. It also transmits the queried enterprise name and related query parameters. If an attacker can influence the runtime environment, deployment configuration, or launche ...[truncated 2256 chars]- Remediation
View remediation
str: if not value: raise RuntimeError("CHINADAAS_BASE_URL is required") parsed = urlparse(value) if parsed.scheme != "https": raise RuntimeError("The API base URL must use HTTPS") if parsed.username or parsed.password or parsed.fragment: raise RuntimeError("The API base URL contains prohibited components") if parsed.hostname not in ALLOWED_HOSTS: raise RuntimeError("The API hostname is not approved") return value.rstrip("/") + "/" BASE_URL ...[truncated 160 chars]
