{"record":{"id":"7f22f6df96d62fbc","repo":"koala73/worldmonitor","slug":"untrusted-source-host","errorCode":null,"errorMessage":"UNTRUSTED_SOURCE_HOST","messagePattern":"UNTRUSTED_SOURCE_HOST","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/imd-cyclone-marine.mjs","lineNumber":755,"sourceCode":"    const { done, value } = await reader.read();\n    if (done) break;\n    size += value.byteLength;\n    if (size > maxBytes) throw new Error(`IMD_RESPONSE_TOO_LARGE:${size}`);\n    chunks.push(value);\n  }\n  return JSON.parse(new TextDecoder().decode(Buffer.concat(chunks.map((chunk) => Buffer.from(chunk)))));\n}\n\nexport async function fetchApprovedImdJson(url, {\n  fetchFn = globalThis.fetch,\n  userAgent = CHROME_UA,\n  maxBytes = IMD_MAX_BYTES,\n  timeoutMs = IMD_TIMEOUT_MS,\n  apiKey = null,\n  apiKeyHeader = 'X-API-Key',\n  apiToken = null,\n} = {}) {\n  if (!isAllowedImdHost(url)) throw new Error('UNTRUSTED_SOURCE_HOST');\n  const headers = { Accept: 'application/json', 'User-Agent': userAgent };\n  if (apiKey) headers[apiKeyHeader] = apiKey;\n  if (apiToken) headers.Authorization = `Bearer ${apiToken}`;\n  const response = await fetchFn(url, {\n    headers,\n    redirect: 'error',\n    signal: AbortSignal.timeout(timeoutMs),\n  });\n  if (!response.ok) {\n    const err = new Error(`HTTP ${response.status}`);\n    err.httpStatus = response.status;\n    throw err;\n  }\n  return readBoundedJsonResponse(response, maxBytes);\n}\n\nexport function createImdProxyFetch(rawProxyUrl, { proxyFetchFn = proxyFetch } = {}) {\n  const proxyUrl = String(rawProxyUrl || '').trim();","sourceCodeStart":737,"sourceCodeEnd":773,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/lib/imd-cyclone-marine.mjs#L737-L773","documentation":"fetchApprovedImdJson() validates every URL against isAllowedImdHost() before fetching: the URL must be https, exactly the allowed IMD hostname, on port 443 (or default), with no embedded credentials. Any URL failing this SSRF guard throws UNTRUSTED_SOURCE_HOST without making a network request. It is an intentional security control, not a network failure.","triggerScenarios":"Calling fetchApprovedImdJson('http://maaalaimaatham/path') (non-https), a mirror/lookalike hostname, a URL with a custom port, a URL with user:pass@ credentials, or a non-URL string.","commonSituations":"Hard-coding an http:// URL during local testing; pointing the fetcher at a staging/mock host or a local proxy URL; data-driven URLs built from user or upstream input that are not on the approved host; typos in the hostname.","solutions":["Use imdProductUrl(product) to build the URL so it always targets the approved IMD host over https.","If you must fetch from another host, host your data or copy it to the approved host; do not bypass the check.","Verify the URL parses as https with the exact allowed hostname, port 443/default, and no credentials: run isAllowedImdHost(url) in a REPL.","Fix typos or stray query components/credentials in the configured URL."],"exampleFix":"// before\nawait fetchApprovedImdJson('http://maaalaimaatham.gov.in/data.json');\n// after\nimport { imdProductUrl } from './imd-cyclone-marine.mjs';\nawait fetchApprovedImdJson(imdProductUrl(product));","handlingStrategy":"validation","validationCode":"import { isAllowedImdHost, imdProductUrl } from './imd-cyclone-marine.mjs';\nconst url = imdProductUrl(product);\nif (!isAllowedImdHost(url)) {\n  throw new Error(`Refusing to fetch non-approved IMD URL: ${url}`);\n}\nawait fetchApprovedImdJson(url);","typeGuard":"function isSafeImdUrl(url) {\n  try {\n    const u = new URL(String(url));\n    return u.protocol === 'https:'\n      && u.hostname.toLowerCase() === 'maaalaimaatham' // exact allowed IMD host\n      && (u.port === '' || u.port === '443')\n      && u.username === '' && u.password === '';\n  } catch {\n    return false;\n  }\n}","tryCatchPattern":"try {\n  const data = await fetchApprovedImdJson(url);\n} catch (err) {\n  if (err.message === 'UNTRUSTED_SOURCE_HOST') {\n    console.error(`Blocked non-approved IMD host: ${url}`);\n    return fallbackSnapshot;\n  }\n  throw err;\n}","preventionTips":["Always build URLs with imdProductUrl(product) instead of string concatenation.","Never construct IMD URLs from untrusted/user-supplied input.","Run isAllowedImdHost() in unit tests for every URL constant you introduce.","Avoid http:// and credential-embedded URLs even in local experiments — they will always be rejected."],"tags":["security","ssrf","validation","url"],"backgroundTag":"invalid-url","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}