T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/run.py:223
- Finding
- Arbitrary Endpoint Can Receive Medical Records and API Credentials<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:223-240`, `scripts/run.py:258` **Vulnerability Type**: Unrestricted remote endpoint configuration and sensitive-data disclosure **Risk Level**: High ### Vulnerable Code ```python def _http_post(url: str, payload: dict[str, Any], headers: dict[str, str], *, timeout: int = 0) -> Any: data = json.dumps(payload, ensure_ascii=False).encode("utf-8") req = urllib.request.Request( url=url, data=data, method="POST", headers={"Content-Type": "application/json", **{key: value for key, value in headers.items() if value}}, ) try: opener = urllib.request.urlopen(req) if not timeout else urllib.request.urlopen(req, timeout=timeout) with opener as resp: body = resp.read().decode("utf-8", errors="replace") return json.loads(body) except urllib.error.HTTPError as exc: detail = exc.read().decode("utf-8", errors="replace") raise RuntimeError(f"HTTP {exc.code}: {detail}") from exc except urllib.error.URLError as exc: raise RuntimeError(f"Network error: {exc}") from exc def call_llm(prompt: str, *, base: str, model: str, appkey: str, timeout: int) -> str: url = f"{base.rstrip('/')}/chat/completions" headers = {"Authorization": f"Bearer {appkey}"} if appkey else {} payload = { "model": model, "messages": [{"role": "user", "content": prompt}], "temperature": 0, } response = _http_post(url, payload, headers, timeout=timeout) ``` The destination is exposed through an unrestricted command-line argument: ```python parser.add_argument("--base", default=DEFAULT_LLM_BASE, help=f"Internal LLM base URL (default: {DEFAULT_LLM_BASE}).") ``` ### Technical Analysis The user-controlled `--base` value is concatenated with `/chat/completions` and used directly as the request destination. The implementation does not enforce HTTPS, validate the hostname against an a ...[truncated 1705 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove the `--base` override from production deployments when endpoint customization is unnecessary. 2. If customization is required, parse the URL and enforce: - The `https` scheme. - An explicit allowlist of trusted hostnames. - Approved ports only. - A normalized, expected API path. - Rejection of embedded credentials, fragments, and unexpected query parameters. 3. Disable automatic redirects or validate every redirect target before resending a request. Never forward authorization headers to a different origin. 4. Bind credentials to the intended service where supported, using narrowly scoped and short-lived tokens. 5. Separate endpoint selection from credential selection so a credential cannot be sent to an unrelated host. 6. Add automated tests confirming that HTTP URLs, unapproved hosts, alternate ports, and cross-origin redirects are rejected. 7. Avoid including unnecessary patient identifiers in prompts and apply documented de-identification before transmission. ]]>
