T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/gen.py:29
- Finding
- OpenAI API Credential Can Be Sent to an Arbitrary Environment-Controlled Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/gen.py:29-40`, with credential transmission at `scripts/gen.py:102-108` and invocation at `scripts/gen.py:174-190` **Vulnerability Type**: Unrestricted credential transmission to a configurable network destination **Risk Level**: High ### Vulnerable Code ```python def _api_url() -> str: base = ( os.environ.get("OPENAI_BASE_URL") or os.environ.get("OPENAI_API_BASE") or "https://api.openai.com" ).rstrip("/") if base.endswith("/v1"): return f"{base}/images/generations" return f"{base}/v1/images/generations" ``` The selected API key is placed in an authorization header without validating the destination: ```python def _post_json(url: str, api_key: str, payload: dict, timeout_s: int) -> dict: body = json.dumps(payload).encode("utf-8") req = urllib.request.Request( url, data=body, headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", }, method="POST", ) try: with urllib.request.urlopen(req, timeout=timeout_s) as resp: raw = resp.read() ``` The environment-derived URL and API key are then used together: ```python api_key = args.api_key or os.environ.get("OPENAI_API_KEY") if not api_key: print("missing OPENAI_API_KEY (or --api-key)", file=sys.stderr) return 2 # ... url = _api_url() items: list[dict] = [] for i, prompt in enumerate(prompts, 1): payload = { "model": args.model, "prompt": prompt, "size": args.size, "quality": args.quality, "n": 1, "response_format": "b64_json", } data = _post_json(url=url, api_key=api_key, payload=payload, timeout_s=args.timeout) ``` ### Technical Analysis The script accepts `OPENAI_BASE_URL` and `OPENAI_API_BASE` as authoritative network destinations. It does not validate that the resulting URL: - Uses HTTPS. - Resolves to ...[truncated 2097 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Default to a fixed endpoint such as `https://api.openai.com/v1/images/generations`. 2. If custom endpoints are unnecessary, remove support for `OPENAI_BASE_URL` and `OPENAI_API_BASE`. 3. If compatible endpoints are required: - Require explicit command-line opt-in rather than silently trusting ambient environment variables. - Parse the URL with `urllib.parse.urlsplit`. - Require the `https` scheme. - Maintain an explicit allowlist of approved hostnames and ports. - Reject embedded credentials, fragments, unexpected paths, loopback addresses, and private-network destinations. 4. Use a separate credential for each compatible provider instead of automatically forwarding `OPENAI_API_KEY`. 5. Display the destination hostname and request user confirmation when a non-default provider is selected. 6. Document all custom endpoint behavior and the fact that prompts and credentials will be transmitted to the selected provider. 7. Consider using the official OpenAI client with a pinned, validated base URL and established transport protections. ]]>
