{"record":{"id":"bcd95b182d24389c","repo":"santifer/career-ops","slug":"himalayas-untrusted-hostname-parsed-hostname","errorCode":null,"errorMessage":"himalayas: untrusted hostname \"${parsed.hostname}\" - must be ${TRUSTED_HOST}","messagePattern":"himalayas: untrusted hostname \"(.+?)\" - must be (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/himalayas.mjs","lineNumber":24,"sourceCode":"// full feed is fetched so scan.mjs's title_filter / location_filter can do\n// the local gating consistently with other zero-token board providers.\n//\n// Wire in via a `job_boards:` entry with `provider: himalayas`.\n\nconst FEED_URL = 'https://himalayas.app/jobs/api?limit=50';\nconst TRUSTED_HOST = 'himalayas.app';\n\n/** @param {string} url */\nfunction assertHimalayasUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`himalayas: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`himalayas: URL must use HTTPS: ${url}`);\n  if (parsed.hostname !== TRUSTED_HOST) {\n    throw new Error(`himalayas: untrusted hostname \"${parsed.hostname}\" - must be ${TRUSTED_HOST}`);\n  }\n  return url;\n}\n\nfunction cleanText(value) {\n  return typeof value === 'string' ? value.trim() : '';\n}\n\nfunction cleanHimalayasUrl(value) {\n  const raw = cleanText(value);\n  if (!raw) return '';\n  try {\n    const parsed = new URL(raw);\n    const host = parsed.hostname.toLowerCase();\n    const trusted = host === TRUSTED_HOST || host.endsWith(`.${TRUSTED_HOST}`);\n    return parsed.protocol === 'https:' && trusted ? parsed.href : '';\n  } catch {\n    return '';","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/himalayas.mjs#L6-L42","documentation":"The himalayas provider validates every URL it will request against a single allowlisted hostname (TRUSTED_HOST, himalayas.app). assertHimalayasUrl parses the URL, requires HTTPS, and throws this error when the hostname does not exactly match the trusted host. This is an SSRF guard: the scanner must never be pointed at an arbitrary host.","triggerScenarios":"assertHimalayasUrl is called with a URL whose parsed hostname differs from TRUSTED_HOST — e.g. a portals.yml entry pointing at a mirror domain, a staging host like himalayas.app.evil.com, or a http->https proxy domain.","commonSituations":"A developer adds a custom portals.yml entry with a wrong/typo'd careers_url; a test substitutes a mock server URL (localhost:3000) without overriding TRUSTED_HOST; an environment variable or config change rewrites the feed host.","solutions":["Fix the URL so its hostname is exactly the TRUSTED_HOST (himalayas.app) and uses https://","If you need to test against a mock server, override/stub TRUSTED_HOST in the test setup instead of passing a local URL","Remove or disable the misconfigured portals.yml entry so scan.mjs does not attempt it"],"exampleFix":"// before\nassertHimalayasUrl('https://api.himalayas.example.com/feed');\n// after\nassertHimalayasUrl('https://himalayas.app/feed');","handlingStrategy":"validation","validationCode":"function isSafeHimalayasUrl(url, trusted = 'himalayas.app') {\n  try {\n    const u = new URL(url);\n    return u.protocol === 'https:' && u.hostname === trusted;\n  } catch { return false; }\n}\n// call before scan: if (!isSafeHimalayasUrl(entry.careers_url)) skip entry;","typeGuard":"const isStringUrl = (v) => typeof v === 'string' && v.length > 0;\nconst isHttps = (u) => u.protocol === 'https:';\nconst isTrustedHost = (u, host) => u.hostname === host;","tryCatchPattern":"try {\n  assertHimalayasUrl(url);\n} catch (err) {\n  if (String(err.message).startsWith('himalayas:')) {\n    console.warn(`skipping misconfigured himalayas entry: ${err.message}`);\n    return null;\n  }\n  throw err;\n}","preventionTips":["Keep careers_url values in portals.yml pointing exactly at the official host","Copy URLs from the provider's own docs, not from redirects or mirrors","Lint portals.yml entries for hostname allowlist compliance before running scans","In tests, stub TRUSTED_HOST instead of injecting localhost URLs"],"tags":["ssrf-protection","url-validation","config","security"],"backgroundTag":"invalid-url","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}