T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/client.py:17
- Finding
- Unrestricted API Destination Can Receive User Identity and Sensitive Pipeline Data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/client.py:17-58` **Vulnerability Type**: Unvalidated outbound network destination and sensitive-data disclosure **Risk Level**: High ### Vulnerable Code ```python def __init__(self, domain_account: Optional[str] = None, bff_url: Optional[str] = None): self.domain_account = domain_account or os.getenv('DEVOPS_DOMAIN_ACCOUNT') self.bff_url = bff_url or os.getenv('DEVOPS_BFF_URL') if not self.domain_account: raise ValueError("Domain account is required. Set DEVOPS_DOMAIN_ACCOUNT.") if not self.bff_url: raise ValueError("BFF URL is required. Set DEVOPS_BFF_URL environment variable.") self.session = requests.Session() self.session.headers.update({'Content-Type': 'application/json'}) def _request(self, method: str, endpoint: str, data: Optional[Dict] = None, params: Optional[Dict] = None) -> Dict[str, Any]: url = f"{self.bff_url}{endpoint}" headers = { 'X-User-Account': self.domain_account } try: if method.upper() == 'GET': response = self.session.get(url, params=params, headers=headers) elif method.upper() == 'POST': response = self.session.post(url, json=data, headers=headers) elif method.upper() == 'DELETE': response = self.session.delete(url, headers=headers) ``` ### Technical Analysis The client obtains the API base URL directly from the `DEVOPS_BFF_URL` environment variable and concatenates it with privileged API endpoints. It does not parse or validate the URL's scheme, hostname, port, user-information component, or resolved network address. Every request automatically includes the user's domain account in `X-User-Account`. POST requests can additionally contain complete pipeline configurations, task parameters, repository URLs, branch information, package metadata, deployment settings, and execution parameters. Outbound network access is necessary for t ...[truncated 2057 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse the base URL with `urllib.parse.urlsplit()` before creating the client. 2. Require the `https` scheme and reject plaintext HTTP. 3. Maintain an explicit allowlist of approved DevOps API hostnames. 4. Reject URLs containing user information, fragments, unexpected paths, or unauthorized ports. 5. Resolve the hostname and reject loopback, link-local, multicast, private, or otherwise prohibited addresses unless an explicitly approved internal endpoint requires them. 6. Normalize the configured base URL and safely join fixed API paths rather than using unrestricted string concatenation. 7. Disable automatic redirects for sensitive requests or validate every redirect destination against the same allowlist. 8. Configure connection and read timeouts for all requests. 9. Use a scoped authentication mechanism rather than treating a caller-supplied account header as sufficient authorization. 10. Document the exact approved destination and fail closed when validation cannot be completed. A hardened implementation should resemble: ```python from urllib.parse import urlsplit import ipaddress import socket APPROVED_HOSTS = {"one-dev.iflytek.com"} parsed = urlsplit(self.bff_url) if parsed.scheme != "https": raise ValueError("DEVOPS_BFF_URL must use HTTPS") if parsed.hostname not in APPROVED_HOSTS: raise ValueError("DEVOPS_BFF_URL host is not approved") if parsed.username or parsed.password or parsed.fragment: raise ValueError("Invalid DEVOPS_BFF_URL") ``` Requests should also use explicit timeouts and constrained redirects: ```python response = self.session.post( url, json=data, headers=headers, timeout=(5, 30), allow_redirects=False ) ``` ]]>
