T09 · Insecure Skill Coding Practices
- Location
- scripts/dl-api.py:15
- Finding
- Unrestricted API Endpoint Configuration Can Expose Credentials and Sensitive Payloads<![CDATA[ ## Vulnerability Details **File Location**: `scripts/dl-api.py:15-22, 32-35, 55-58`; `scripts/dl-pipeline.py:18-25, 32-35, 39-42` **Vulnerability Type**: Unvalidated remote endpoint and transport downgrade **Risk Level**: Medium ### Vulnerable Code #### `scripts/dl-api.py` ```python BASE_URL = os.environ.get( "DIGITAL_LABOUR_API_URL", "https://bitrage-labour-api-production.up.railway.app", ).rstrip("/") API_KEY = os.environ.get("DIGITAL_LABOUR_API_KEY", "") ``` ```python def _headers(): h = {"Content-Type": "application/json", "Accept": "application/json"} if API_KEY: h["X-Api-Key"] = API_KEY return h ``` ```python def _post(path, payload): """POST request with JSON body, returns parsed JSON.""" url = f"{BASE_URL}{path}" data = json.dumps(payload).encode("utf-8") req = urllib.request.Request(url, data=data, headers=_headers(), method="POST") ``` #### `scripts/dl-pipeline.py` ```python BASE_URL = os.environ.get( "DIGITAL_LABOUR_API_URL", "https://bitrage-labour-api-production.up.railway.app", ).rstrip("/") API_KEY = os.environ.get("DIGITAL_LABOUR_API_KEY", "") ``` ```python def _headers(): h = {"Content-Type": "application/json", "Accept": "application/json"} if API_KEY: h["X-Api-Key"] = API_KEY return h ``` ```python def _post(path, payload): url = f"{BASE_URL}{path}" data = json.dumps(payload).encode("utf-8") req = urllib.request.Request(url, data=data, headers=_headers(), method="POST") ``` ### Technical Analysis Both clients accept `DIGITAL_LABOUR_API_URL` without validating its URL scheme, hostname, port, or destination. The same request-building logic automatically attaches the value of `DIGITAL_LABOUR_API_KEY` as an `X-Api-Key` header. Consequently, setting the base URL to an attacker-controlled endpoint causes the client to transmit the API key and complete JSON request body to that endpoint. A URL using plain HTTP also permits network-positioned ...[truncated 2617 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Require encrypted transport** - Parse the configured endpoint with `urllib.parse.urlsplit`. - Reject every scheme other than `https`. - Reject URLs containing embedded credentials, fragments, or unexpected ports. 2. **Restrict trusted destinations** - Maintain an explicit allowlist of approved API hostnames. - Compare normalized hostnames exactly rather than using suffix or substring checks. - If custom endpoints are a required feature, require an explicit opt-in flag and display a warning before transmitting credentials or sensitive data. 3. **Bind credentials to trusted hosts** - Add `X-Api-Key` only when the normalized destination hostname is approved. - Fail closed instead of silently sending authenticated requests to arbitrary hosts. 4. **Centralize secure endpoint validation** - Implement a shared URL-validation function and use it in both Python clients. - Validate the endpoint before processing agent inputs or reading sensitive batch data. 5. **Reduce exposure** - Avoid submitting unnecessary personal, financial, or confidential data. - Redact sensitive fields before constructing requests. - Use narrowly scoped, revocable API keys and rotate any key suspected of exposure. A suitable validation pattern should enforce all relevant properties before creating a request: ```python from urllib.parse import urlsplit TRUSTED_API_HOSTS = { "bitrage-labour-api-production.up.railway.app", } def validate_base_url(value): parsed = urlsplit(value) if parsed.scheme != "https": raise ValueError("The API URL must use HTTPS") if parsed.username or parsed.password: raise ValueError("Embedded URL credentials are not permitted") if parsed.fragment: raise ValueError("URL fragments are not permitted") if parsed.hostname not in TRUSTED_API_HOSTS: raise ValueError("Untrusted API hostname") if parsed.port not in (None, 443): ra ...[truncated 189 chars]
