T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/teable_base.py:18
- Finding
- Bearer Token Disclosure and SSRF Through Insufficient Endpoint Validation<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/teable_base.py:18-55, 105-123` - `scripts/teable_record.py:18-58, 115-139` - `scripts/teable_dashboard.py:17-36, 47-62` - `scripts/teable_space.py:18-39, 50-65` - `scripts/teable_table.py:17-36, 47-62` - `scripts/teable_trash.py:17-36, 47-62` **Vulnerability Type**: Insufficient URL validation, plaintext credential transmission, and server-side request forgery **Risk Level**: High ### Vulnerable Code The following representative implementation appears in `scripts/teable_base.py` and is substantially duplicated across all six clients: ```python ALLOWED_SCHEMES = ["https", "http"] def validate_url(url: str) -> str: """ Validate URL to prevent SSRF and credential exfiltration. Args: url: URL to validate Returns: Validated URL Raises: ValueError: If URL is invalid or potentially malicious """ if not url: return url try: parsed = urlparse(url) if parsed.scheme.lower() not in ALLOWED_SCHEMES: raise ValueError( f"Invalid URL scheme: '{parsed.scheme}'. " f"Only {ALLOWED_SCHEMES} are allowed." ) if not parsed.netloc: raise ValueError(f"Invalid URL: missing domain in '{url}'") if parsed.scheme.lower() == "http": print( "WARNING: Using HTTP instead of HTTPS. " "Your API key will be transmitted in plaintext!", file=sys.stderr ) return url.rstrip("/") except Exception as e: raise ValueError(f"Invalid TEABLE_URL '{url}': {e}") ``` The validated URL is then combined with the API path and used in a session that always contains the bearer token: ```python raw_url = base_url or os.getenv("TEABLE_URL") or DEFAULT_BASE_URL self.base_url = validate_url(raw_url) self.api_key = api_key or ...[truncated 3791 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Require HTTPS for all authenticated requests: ```python ALLOWED_SCHEMES = {"https"} ``` 2. If plaintext HTTP is required for local development, gate it behind an explicit unsafe option and do not attach the bearer token automatically. 3. Use an administrator-managed allowlist for permitted Teable hosts. Do not treat any syntactically valid HTTP or HTTPS URL as trusted. 4. Reject URLs containing embedded user information, unexpected ports, fragments, or malformed hostnames. 5. Resolve the hostname before sending credentials and reject loopback, private, link-local, multicast, reserved, and unspecified IP addresses unless a specific self-hosted deployment explicitly allows them. 6. Revalidate every resolved destination and redirect target. Prefer disabling redirects for authenticated API requests unless redirects are required: ```python response = self.session.request( method, url, allow_redirects=False, timeout=(5, 30), **kwargs ) ``` 7. Attach the `Authorization` header only after validating the final destination rather than storing it in a session that may be reused for arbitrary URLs. 8. Use separate configuration for trusted self-hosted deployments and document that adding a host grants it access to the API token. 9. Add security tests covering attacker-controlled hosts, HTTP URLs, IPv4 and IPv6 loopback addresses, private ranges, link-local addresses, hostname resolution to private IPs, and redirects to untrusted origins. ]]>
