T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/story_engine.py:76
- Finding
- API Credentials and Narrative Content Can Be Sent to an Untrusted or Cleartext Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/story_engine.py`, lines 76–111 **Vulnerability Type**: Unvalidated remote endpoint and potentially insecure transmission of sensitive information **Risk Level**: High ### Vulnerable Code ```python def __init__( self, api_key: str, model: str = "gpt-4o", base_url: str = "https://api.openai.com/v1", timeout: int = 120, ): self.api_key = api_key self.model = model self.base_url = base_url.rstrip("/") self.timeout = timeout def generate(self, prompt: str, **kwargs) -> str: import json import urllib.request import urllib.error temperature = kwargs.get("temperature", 0.7) max_tokens = kwargs.get("max_tokens", 4096) body = json.dumps({ "model": self.model, "messages": [{"role": "user", "content": prompt}], "temperature": temperature, "max_tokens": max_tokens, }).encode("utf-8") req = urllib.request.Request( f"{self.base_url}/chat/completions", data=body, headers={ "Content-Type": "application/json", "Authorization": f"Bearer {self.api_key}", }, method="POST", ) try: with urllib.request.urlopen(req, timeout=self.timeout) as resp: ``` ### Technical Analysis `OpenAIProvider` accepts a caller-controlled `base_url` and uses it without validating its scheme or destination. The implementation therefore permits cleartext `http://` endpoints and arbitrary remote hosts. Every request places the API key in the `Authorization` header and the generated prompt in the HTTP request body. Depending on the operation, prompts may contain unpublished chapter text, character profiles, world rules, plot details, causal chains, and audit context. Relevant remote-call paths include chapter generation and rewriting, presentation repair, causal diagnosis, prose review, and world-consistency review. The network behavior is optional rather than c ...[truncated 1890 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Require secure transport** - Parse `base_url` with `urllib.parse.urlparse`. - Reject URLs whose scheme is not `https`. - If local development endpoints must be supported, permit cleartext HTTP only for explicitly approved loopback addresses and only through a clearly named development option. 2. **Restrict endpoint destinations** - Default to a documented allowlist of trusted provider hosts. - Require explicit user approval before allowing a custom host. - Reject embedded credentials, malformed hosts, unexpected ports, and ambiguous URL forms. 3. **Prevent unsafe redirects** - Ensure requests carrying `Authorization` cannot be redirected to a different or less secure origin. - Either disable redirects for authenticated requests or validate every redirect target before forwarding credentials. 4. **Minimize transmitted data** - Send only the narrative fields required for the selected operation. - Avoid transmitting entire character databases, chapters, or world state when a smaller excerpt is sufficient. - Add optional redaction for secrets, personal data, and confidential metadata. 5. **Improve disclosure and consent** - Clearly document which methods invoke the provider and what categories of data each method transmits. - Display or log the destination host before the first remote request. - Require confirmation when a custom remote provider is first attached. 6. **Limit credential exposure** - Use provider credentials scoped to the minimum required models, permissions, quotas, and billing limits. - Avoid retaining API keys longer than necessary. - Never include the key in error messages, logs, serialized engine state, or generated reports. 7. **Add security tests** - Verify rejection of `http://` URLs, unapproved hosts, malformed URLs, and cross-origin redirects. - Verify that credentials are sent only to the approved HTTPS origin. ]]>
