{"record":{"id":"701604688e40cf9d","repo":"santifer/career-ops","slug":"jobvite-untrusted-hostname-parsed-hostname","errorCode":null,"errorMessage":"jobvite: untrusted hostname \"${parsed.hostname}\" — must be ${BOARD_HOST} or ${FEED_HOST}","messagePattern":"jobvite: untrusted hostname \"(.+?)\" — must be (.+?) or (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/jobvite.mjs","lineNumber":108,"sourceCode":"// network failure. Sized to absorb a genuinely big tenant on a slow link; the\n// board page (a normal HTML document) keeps the default.\nconst FEED_TIMEOUT_MS = 45_000;\n\n/**\n * Pin a URL to the two known Jobvite hosts over HTTPS.\n * @param {string} url\n */\nfunction assertJobviteHost(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`jobvite: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:')\n    throw new Error(`jobvite: URL must use HTTPS: ${url}`);\n  if (!ALLOWED_HOSTS.has(parsed.hostname))\n    throw new Error(`jobvite: untrusted hostname \"${parsed.hostname}\" — must be ${BOARD_HOST} or ${FEED_HOST}`);\n  return url;\n}\n\n// NaN-safe Date.parse → epoch ms.\n/** @param {string} value */\nfunction toEpochMs(value) {\n  if (!value) return undefined;\n  const parsed = Date.parse(value);\n  return Number.isNaN(parsed) ? undefined : parsed;\n}\n\n/**\n * The vanity slug from a Jobvite careers URL, or null.\n * Only used to build the board URL for eId discovery.\n *\n * @param {import('./_types.js').PortalEntry} entry\n * @returns {string | null}\n */","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/jobvite.mjs#L90-L126","documentation":"assertJobviteHost() accepts only two hostnames: BOARD_HOST (jobs.jobvite.com) and FEED_HOST (Jobvite's feed host). Any other parsed hostname throws this untrusted-hostname error, preventing SSRF through a user-controlled careers_url or api URL.","triggerScenarios":"A jobvite entry whose api:/careers_url points at another domain — e.g. search.jobvite.com, a custom vanity domain, or a lookalike host; or a slug was embedded into the wrong base URL by custom code.","commonSituations":"Copying a jobvite URL from a company's own website that uses a vanity CNAME; using search.jobvite.com?invalid=1 style links from a retired board; typo'd hostnames.","solutions":["Point the URL at https://jobs.jobvite.com/<company-slug> (or the feed host) exactly.","Set company_eid: on the entry so fetch() builds the allowlisted URL from the ID rather than trusting a custom host.","If you have a legitimate third host, update ALLOWED_HOSTS / BOARD_HOST / FEED_HOST constants in providers/jobvite.mjs deliberately."],"exampleFix":"// before (portals.yml)\nprovider: jobvite\ncareers_url: https://careers.acme.com/jobs\n// after\nprovider: jobvite\ncareers_url: https://jobs.jobvite.com/acme","handlingStrategy":"validation","validationCode":"const host = new URL(entry.careers_url).hostname;\nif (host !== 'jobs.jobvite.com' && host !== FEED_HOST) throw new Error(`${host} is not a jobvite board host`);","typeGuard":"const isJobviteUrl = (s) => { try { return ['jobs.jobvite.com', FEED_HOST].includes(new URL(s).hostname); } catch { return false; } };","tryCatchPattern":"try {\n  await provider.fetch(entry, ctx);\n} catch (e) {\n  if (e.message.includes('jobvite: untrusted hostname')) {\n    entry.careers_url = `https://jobs.jobvite.com/${entry.slug ?? entry.name.toLowerCase()}`;\n  }\n}","preventionTips":["Use only jobs.jobvite.com board URLs or the official feed host in config.","If a company uses a vanity careers domain, find its underlying jobs.jobvite.com board instead.","Set company_eid: so the provider constructs the URL itself."],"tags":["ssrf","allowlist","url-validation","jobvite"],"backgroundTag":"invalid-config-value","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"}