T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/hostex-client.mjs:22
- Finding
- Arbitrary API Base URL Can Exfiltrate the Hostex Access Token and Private Data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/hostex-client.mjs:22-29`, `scripts/hostex-client.mjs:44-70` **Vulnerability Type**: Unrestricted credential-bearing network destination **Risk Level**: High ### Vulnerable Code ```js export function buildUrl(path, query = {}) { const base = getEnv('HOSTEX_BASE_URL', DEFAULT_BASE_URL); // Important: preserve base path segments (e.g. https://api.hostex.io/v3) // new URL('/room_types', 'https://api.hostex.io/v3') would drop /v3. const baseUrl = new URL(base.endsWith('/') ? base : `${base}/`); const rel = path.startsWith('/') ? path.slice(1) : path; const u = new URL(rel, baseUrl); ``` ```js export async function hostexRequest({ method, path, query, json, headers, timeoutMs = 30000, retries = 2, }) { const token = getEnv('HOSTEX_ACCESS_TOKEN'); if (!token) throw new Error('Missing HOSTEX_ACCESS_TOKEN'); const url = buildUrl(path, query); const reqHeaders = { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Hostex-Access-Token': token, ...headers, }; const attempt = async (n) => { const controller = new AbortController(); const t = setTimeout(() => controller.abort(new Error('timeout')), timeoutMs); try { let res; try { res = await fetch(url, { method, headers: reqHeaders, body: json ? JSON.stringify(json) : undefined, signal: controller.signal, }); ``` ### Technical Analysis The API client obtains its destination from the unrestricted `HOSTEX_BASE_URL` environment variable. It does not validate the URL scheme or require the destination hostname to be `api.hostex.io`. Every request attaches `HOSTEX_ACCESS_TOKEN` as the `Hostex-Access-Token` header. Write requests can also carry guest names, email addresses, telephone numbers, messages, reservation details, property identifiers, availability information, and pricing data in the request body. Send ...[truncated 1796 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove `HOSTEX_BASE_URL` from production operation and always use the fixed official endpoint: ```js const DEFAULT_BASE_URL = 'https://api.hostex.io/v3'; ``` 2. If an override is necessary for testing, require a separate explicit development-only option and validate it before loading the token: - Require the `https:` protocol. - Allowlist exact trusted hostnames. - Reject embedded credentials, unexpected ports, and ambiguous hostname forms. - Keep localhost or test endpoints behind an explicit test mode that uses a non-production token. 3. Validate the final URL immediately before every credential-bearing request: ```js function validateApiUrl(url) { if (url.protocol !== 'https:' || url.hostname !== 'api.hostex.io') { throw new Error('Refusing to send Hostex credentials to an untrusted destination'); } } ``` 4. Disable automatic cross-origin redirects for authenticated requests, or validate every redirect destination before resending credentials. For example, use `redirect: 'error'` if redirects are not required. 5. Continue recommending read-only, narrowly scoped PATs. Use separate credentials for test and production environments and rotate any PAT that may have been sent to an untrusted endpoint. 6. Add automated tests proving that credential-bearing requests are rejected for: - Plain HTTP URLs - Unapproved domains - Lookalike or subdomain-confusion hostnames - Unexpected ports - Cross-origin redirects ]]>
