T09 · Insecure Skill Coding Practices
- Location
scripts/api.py:82- Finding
Unrestricted Custom Engine URL Can Exfiltrate Stored API Credentials and Applicant Data
- Content
View full analysis
Vulnerability Details
File Location:
scripts/api.py:16-18, 62-68, 82-92
Vulnerability Type: Server destination injection leading to credential and sensitive-data disclosure
Risk Level: HighVulnerable Code
python ENGINE_URL = os.environ.get("STUDY_ENGINE_URL", "https://compliancehub.cn") API_PREFIX = "/api/study" CONFIG_DIR = Path(os.environ.get("STUDY_CONFIG_DIR", Path.home() / ".study-abroad"))python def api_key() -> str | None: """Registered user API key: prefer STUDY_API_KEY, otherwise use KEY_FILE.""" k = os.environ.get("STUDY_API_KEY") or "" if k: return k.strip() if KEY_FILE.exists(): k = KEY_FILE.read_text().strip() return k or None return Nonepython def request(method: str, path: str, params=None, body=None) -> dict: """Unified request returning parsed JSON response data.""" url = ENGINE_URL.rstrip("/") + API_PREFIX + path headers = {"x-anon-id": anon_id(), "Content-Type": "application/json"} if api_key(): headers["x-api-key"] = api_key() payload = json.dumps(body, ensure_ascii=False).encode() if body is not None else None if _http(): httpx = _http() try: r = httpx.request(method, url, params=params, headers=headers, content=payload, timeout=20, trust_env=False)Technical Analysis
The
STUDY_ENGINE_URLenvironment variable controls the destination of all API requests without hostname validation, scheme validation, or an enforced origin allowlist. At the same time,request()automatically attaches the registered user's API key and anonymous identifier to every request.Consequently, an attacker who can influence the process environment or command invocation can redirect requests to an attacker-controlled HTTP or HTTPS server. The client will then transmit the
x-api-keyheader,x-anon-idh ...[truncated 2241 chars]- Remediation
View remediation
Remediation Suggestions
-
Enforce an explicit destination allowlist. Production requests should accept only the exact trusted HTTPS origin:
python from urllib.parse import urlparse ALLOWED_HTTPS_ORIGINS = {"https://compliancehub.cn"} ALLOWED_LOCAL_HOSTS = {"127.0.0.1", "localhost", "::1"} def validate_engine_url(value: str) -> str: parsed = urlparse(value) origin = f"{parsed.scheme}://{parsed.netloc}" if origin in ALLOWED_HTTPS_ORIGINS: return value.rstrip("/") if parsed.scheme == "http" and parsed.hostname in ALLOWED_LOCAL_HOSTS: return value.rstrip("/") raise ValueError("Untrusted STUDY_ENGINE_URL") -
Restrict plaintext HTTP to loopback development endpoints. Require HTTPS for every non-loopback destination.
-
Do not automatically attach credentials to custom endpoints. If custom remote engines must be supported, require a separate endpoint-specific credential and explicit user confirmation.
-
Validate the final request origin immediately before adding
x-api-key. Credential attachment should depend on the validated origin rather than merely on the existence of an API key. -
Disable redirects or independently validate every redirect destination before forwarding authentication headers.
-
Warn users prominently when a non-default engine is configured, displaying the exact destination before any profile or document data is transmitted.
-
Add automated security tests confirming that:
- Arbitrary external HTTP and HTTPS origins are rejected.
- HTTP is accepted only for loopback development.
- Credentials are never sent to untrusted origins.
- Redirects cannot move authenticated requests to another origin.
-
