T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/run.py:23
- Finding
- Untrusted API Base URL Can Exfiltrate Credentials and Submitted Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:13`, `scripts/run.py:23-38`, `scripts/run.py:82-88`, and `scripts/run.py:129` **Vulnerability Type**: User-controlled authenticated request destination **Risk Level**: Medium ### Vulnerable Code ```python DEFAULT_BASE_URL = "https://ai-skills.ai" ``` ```python def build_base_url(): return os.getenv("AISKILLS_BASE_URL", DEFAULT_BASE_URL).rstrip("/") def build_headers(): api_key = os.getenv("AISKILLS_API_KEY", "").strip() tenant_id = os.getenv("AISKILLS_TENANT_ID", "default").strip() or "default" if not api_key: fail("AISKILLS_API_KEY is required") return { "Content-Type": "application/json", # Cloudflare blocks urllib's default Python user agent for this endpoint. "User-Agent": "ai-skills-runner/1.0 (+https://ai-skills.ai)", "Accept": "application/json", "X-API-Key": api_key, "X-Tenant-Id": tenant_id, } ``` ```python def request_json(method, path, payload): body = json.dumps(payload).encode("utf-8") req = urllib.request.Request( f"{build_base_url()}{path}", data=body, method=method, headers=build_headers(), ) ``` ```python response = request_json("POST", EXECUTE_PATH, {"skillId": SKILL_ID, "params": params}) ``` ### Technical Analysis The request destination is taken directly from the `AISKILLS_BASE_URL` environment variable without validating its scheme or hostname. The same request always includes the `AISKILLS_API_KEY` and `AISKILLS_TENANT_ID` headers. Consequently, a process environment that sets `AISKILLS_BASE_URL` to an attacker-controlled server causes the runner to transmit authentication data and the complete skill input to that server. The input can include unpublished article text, uploaded-material references, audience information, conversion goals, and confidential brand requirements. The implementation also does not explicitly require HTTPS. A con ...[truncated 2195 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Enforce HTTPS** - Parse the configured URL with `urllib.parse.urlparse`. - Reject all schemes other than `https`. - Reject URLs containing embedded credentials, fragments, or malformed hostnames. 2. **Allowlist trusted destinations** - Default to `https://ai-skills.ai`. - Only send `X-API-Key` and `X-Tenant-Id` when the normalized hostname exactly matches an approved hostname. - Avoid suffix-based checks that could accept domains such as `ai-skills.ai.attacker.example`. 3. **Separate custom endpoints from production credentials** - If custom endpoints are needed for development, require an explicit development-mode option. - Use separate test credentials for custom endpoints. - Never automatically reuse production credentials for arbitrary destinations. 4. **Fail closed** - Terminate execution when URL validation fails. - Validate the destination before constructing authentication headers or serializing sensitive request data. 5. **Harden deployment configuration** - Restrict who can modify environment variables in CI/CD, containers, and service definitions. - Store API keys in a managed secret store and scope them to the minimum required permissions. - Rotate the API key if execution with an untrusted base URL may already have occurred. 6. **Add regression tests** - Verify that HTTP endpoints are rejected. - Verify that unapproved hostnames and deceptive subdomains are rejected. - Verify that authentication headers are never attached to custom or untrusted endpoints. ]]>
