T09 · Insecure Skill Coding Practices
- Location
scripts/showmeai_core/outputs.py:85- Finding
Bearer Credential Forwarded to Arbitrary Result URLs
- Content
View full analysis
Path: parsed = urllib.parse.urlparse(url) if parsed.scheme not in {"http", "https"}: raise SkillError("OUTPUT_URL_INVALID", "Only HTTP(S) result URLs are accepted.") suffix = Path(parsed.path).suffix.lstrip(".") if not suffix: suffix = mimetypes.guess_extension("application/octet-stream", strict=False).lstrip(".") or "bin" raw, _headers = client.request("GET", url, timeout=300) return self.write_bytes(raw, stem, suffix, filename) ``` ```python # scripts/showmeai_core/http.py:74-90 def request( self, method: str, url: str, *, body: bytes | None = None, content_type: str = "application/json", timeout: int = 300, ) -> tuple[bytes, dict[str, str]]: headers = {"Authorization": f"Bearer {self.api_key}"} if body is not None: headers["Content-Type"] = content_type last_error: Exception | None = None for attempt in range(self.retries + 1): request = urllib.request.Request(url, method=method, headers=headers, data=body) try: with urllib.request.urlopen(request, timeout=timeout) as response: return response.read(), dict(response.headers.items()) ``` ### Technical Analysis `OutputManager.download()` accepts every syntactically valid HTTP or HTTPS URL. It does not verify that the destination belongs to the configured ShowMeAI API origin or to an explicitly trusted media host. The accepted URL is passed to `ApiClient.request()`, which unconditionally adds the ShowMeAI API key as a bearer credential. Consequently, ...[truncated 1948 chars]- Remediation
View remediation
tuple[bytes, dict[str, str]]: request = urllib.request.Request(url, method="GET") with urllib.request.urlopen(request, timeout=timeout) as response: return response.read(), dict(response.headers.items()) ``` 2. Do not attach the API bearer credential to arbitrary result URLs. Use authenticated downloads only when the normalized destination origin exactly matches the validated API origin. 3. Compare origins using parsed scheme, hostname, and effective port rather than string-prefix matching. 4. Maintain an explicit allowlist if ShowMeAI requires authenticated downloads from documented CDN or object-storage hosts. 5. Disable automatic redirects for credentialed requests or implement redirect handling that strips `Authorization` whenever the destination origin changes. 6. Reject plaintext HTTP result URLs unless there is a documented and explicitly enabled compatibility requirement. 7. Add tests proving that: - Cross-origin result downloads contain no `Authorization` header. - Same-origin authenticated API calls retain the header. - Cross-origin redirects strip the header. - Unsupported URL schemes and embedded URL credentials are rejected. 8. Rotate any API keys that may already have been exposed through cross-origin result downloads. ]]>
