{"record":{"id":"175dcb9f4077197f","repo":"decolua/9router","slug":"xai-discovery-field-host-host-is-not-on-x-ai","errorCode":null,"errorMessage":"xai discovery ${field} host ${host} is not on x.ai","messagePattern":"xai discovery (.+?) host (.+?) is not on x\\.ai","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/lib/oauth/services/xai.js","lineNumber":43,"sourceCode":"\nexport function validateOAuthEndpoint(rawUrl, field) {\n  const value = String(rawUrl || \"\").trim();\n  if (!value) throw new Error(`xai discovery ${field} is empty`);\n\n  let parsed;\n  try {\n    parsed = new URL(value);\n  } catch (err) {\n    throw new Error(`xai discovery ${field} is invalid: ${err.message}`);\n  }\n\n  if (parsed.protocol !== \"https:\") {\n    throw new Error(`xai discovery ${field} must use https: ${value}`);\n  }\n\n  const host = parsed.hostname.toLowerCase().trim();\n  if (host !== \"x.ai\" && !host.endsWith(\".x.ai\")) {\n    throw new Error(`xai discovery ${field} host ${host} is not on x.ai`);\n  }\n\n  return value;\n}\n\n/**\n * Discover authorization + token endpoints. Cached process-wide.\n */\nexport async function discoverEndpoints() {\n  if (cachedDiscovery) return cachedDiscovery;\n\n  try {\n    const res = await fetch(XAI_CONFIG.discoveryUrl, {\n      headers: { Accept: \"application/json\" },\n    });\n    if (res.ok) {\n      const data = await res.json();\n      cachedDiscovery = {","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/lib/oauth/services/xai.js#L25-L61","documentation":"After protocol validation, the hostname must be exactly 'x.ai' or a subdomain ending in '.x.ai' (case-insensitive). Any other host is rejected, defending against lookalike/phishing domains in discovery documents that could capture authorization codes and tokens.","triggerScenarios":"Discovery field points at e.g. https://xai.example.com/..., https://x.ai.evil.io/... (endsWith('.x.ai') is false for this because host is 'x.ai.evil.io'), or a mirror domain like https://x-ai.com/.","commonSituations":"Man-in-the-middle or malicious discovery responses; teams proxying xAI through their own domain; typos in manually configured endpoints.","solutions":["Point the field at a genuine x.ai host (https://x.ai/... or https://api.x.ai/... as applicable).","If you route through a corporate gateway, don't override the OAuth endpoints — proxy at the network layer instead.","Verify the discovery document was fetched over TLS from x.ai, since a foreign host here means the discovery source is wrong or compromised."],"exampleFix":"// before\nXAI_TOKEN_URL=https://auth.mycompany.com/xai/token\n// after\nXAI_TOKEN_URL=https://x.ai/api/oauth/token","handlingStrategy":"validation","validationCode":"function isXaiHost(u) {\n  try {\n    const h = new URL(String(u).trim()).hostname.toLowerCase();\n    return h === 'x.ai' || h.endsWith('.x.ai');\n  } catch { return false; }\n}\nif (!isXaiHost(cfg.tokenUrl)) throw new Error('xAI endpoint host must be x.ai or *.x.ai');","typeGuard":"function isXaiEndpoint(u) {\n  if (typeof u !== 'string') return false;\n  try {\n    const parsed = new URL(u.trim());\n    if (parsed.protocol !== 'https:') return false;\n    const h = parsed.hostname.toLowerCase();\n    return h === 'x.ai' || h.endsWith('.x.ai');\n  } catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["Only consume discovery documents fetched directly over TLS from x.ai.","Never point OAuth endpoints at third-party or internal mirror domains.","Add an allowlist check on any user/admin-supplied endpoint overrides."],"tags":["oauth","xai","security","allowlist","url"],"backgroundTag":"oauth-endpoint-host-mismatch","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}