T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/run_benchmark.py:53
- Finding
- Specification-Controlled Credential Disclosure to Arbitrary HTTPS Endpoints<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run_benchmark.py:53-65, 107-142` **Vulnerability Type**: Arbitrary environment credential disclosure and insufficient outbound endpoint validation **Risk Level**: High ### Vulnerable Code ```python def call_model(base_url, api_key, model, prompt): url = base_url.rstrip('/') + '/chat/completions' normalized_model = normalize_model_name(model, base_url) payload = {'model': normalized_model, 'messages': [{'role': 'user', 'content': prompt}]} raw_payload = json.dumps(payload).encode('utf-8') headers = {'Content-Type': 'application/json'} if api_key: headers['Authorization'] = f'Bearer {api_key}' start = time.time() try: req = urllib.request.Request(url, data=raw_payload, headers=headers, method='POST') with urllib.request.urlopen(req, timeout=180) as resp: data = json.loads(resp.read().decode('utf-8')) ``` ```python def _is_safe_base_url(base_url: str) -> bool: """Basic safety checks. - Require https - Block raw IPs - Block localhost This is not a complete security solution, but it prevents the most common foot-guns that trigger security scanners and protects against accidental exfiltration to an IP/short endpoint. """ if not base_url: return False base_url = base_url.strip() if not base_url.startswith('https://'): return False lowered = base_url.lower() if 'localhost' in lowered or '127.0.0.1' in lowered or '0.0.0.0' in lowered: return False # crude IP check (blocks http(s)://<digits>.<digits>.<digits>.<digits>) import re if re.search(r'https://\d{1,3}(?:\.\d{1,3}){3}(?::\d+)?(?:/|$)', lowered): return False return True def run_single_model(spec: dict, model: str, run_dir: Path, run_id: str): base_url = spec.get('base_url') or os.environ.get('BENCHMARK_BASE_URL') or '' if not _is_safe_base_url(base_url): raise Syste ...[truncated 3243 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Restrict credential selection** - Remove specification-controlled access to arbitrary environment-variable names. - Use only a fixed variable such as `BENCHMARK_API_KEY`, or maintain a small operator-configured allowlist. - Prefer provider-specific, least-privilege credentials rather than exposing the general process environment. 2. **Restrict outbound destinations** - Require an explicit allowlist of trusted provider hostnames. - Require user confirmation before sending a credential to a previously unapproved endpoint. - Bind each credential to its expected hostname and reject mismatched credential-destination combinations. 3. **Implement robust URL validation** - Parse URLs with `urllib.parse.urlsplit()` rather than string-prefix checks. - Require the exact `https` scheme and a valid hostname. - Reject URL user information, fragments, unsupported ports, malformed hosts, and ambiguous encodings. - Resolve every hostname and reject loopback, private, link-local, multicast, reserved, unspecified, and cloud metadata ranges for IPv4 and IPv6. 4. **Control redirects** - Disable automatic redirects for credentialed requests where possible. - If redirects are required, revalidate every target and remove credentials when the origin changes. 5. **Reduce data exposure** - Display the destination hostname and data categories before execution. - Require explicit approval before transmitting potentially sensitive prompts. - Document that specifications are executable security-sensitive configuration and must not be accepted from untrusted sources. 6. **Add security tests** - Test malicious `auth_env` values. - Test IPv6 loopback and private addresses. - Test hostnames resolving to private addresses, DNS rebinding scenarios, encoded IP forms, and redirects to internal services. ]]>
