{"record":{"id":"bc59ba21b44b7a4c","repo":"santifer/career-ops","slug":"teamtailor-untrusted-hostname-parsed-hostname","errorCode":null,"errorMessage":"teamtailor: untrusted hostname \"${parsed.hostname}\" — must be <slug>.teamtailor.com (or set \"provider: teamtailor\" to use a branded careers domain)","messagePattern":"teamtailor: untrusted hostname \"(.+?)\" — must be <slug>\\.teamtailor\\.com \\(or set \"provider: teamtailor\" to use a branded careers domain\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/teamtailor.mjs","lineNumber":42,"sourceCode":"const TEAMTAILOR_HOST_RE = /^([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\\.teamtailor\\.com$/i;\n\n/**\n * Validate a feed URL before fetching. Always HTTPS-only. The hostname is\n * pinned to `*.teamtailor.com` for auto-detected entries; an explicit\n * `provider: teamtailor` entry may use its configured branded host.\n * @param {string} url\n * @param {{ explicit?: boolean }} [opts]\n */\nfunction assertFeedUrl(url, { explicit = false } = {}) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`teamtailor: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`teamtailor: URL must use HTTPS: ${url}`);\n  if (!explicit && !TEAMTAILOR_HOST_RE.test(parsed.hostname)) {\n    throw new Error(`teamtailor: untrusted hostname \"${parsed.hostname}\" — must be <slug>.teamtailor.com (or set \"provider: teamtailor\" to use a branded careers domain)`);\n  }\n  return url;\n}\n\n// Derive the RSS feed URL from a tracked_companies entry by normalizing any\n// path on the configured host to /jobs.rss. Auto-detection (explicit=false)\n// only claims *.teamtailor.com hosts; an explicit `provider: teamtailor` entry\n// (explicit=true) may use a branded careers host. Returns null otherwise.\n/**\n * @param {import('./_types.js').PortalEntry} entry\n * @param {{ explicit?: boolean }} [opts]\n */\nfunction resolveFeedUrl(entry, { explicit = false } = {}) {\n  const raw = entry?.api || entry?.careers_url || '';\n  if (typeof raw !== 'string' || !raw) return null;\n  let parsed;\n  try {\n    parsed = new URL(raw);","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/providers/teamtailor.mjs#L24-L60","documentation":"assertFeedUrl's third check (skipped when explicit=true) requires the hostname to match TEAMTAILOR_HOST_RE, i.e. <slug>.teamtailor.com. A branded careers domain (e.g. careers.acme.com) is rejected unless the entry opts in with provider: teamtailor, which sets explicit=true and bypasses this check. This is the SSRF pin that keeps auto-detection on the trusted *.teamtailor.com host.","triggerScenarios":"An auto-detected entry's careers_url points at a branded domain rather than *.teamtailor.com, and no provider: teamtailor opt-in is set. The provider will not auto-claim an arbitrary branded host.","commonSituations":"A company fronts the same RSS feed on a custom domain; the user added the branded URL expecting detection. The supported workaround is the explicit provider opt-in.","solutions":["Add provider: teamtailor to the entry so the branded host is honored (explicit=true)","Or point careers_url at the canonical https://<slug>.teamtailor.com host","Verify the branded host actually serves /jobs.rss before relying on it"],"exampleFix":"# before\n- name: Acme\n  careers_url: https://careers.acme.com\n# after\n- name: Acme\n  provider: teamtailor\n  careers_url: https://careers.acme.com","handlingStrategy":"validation","validationCode":"// For branded hosts, require the explicit opt-in before fetch.\nconst TT_RE = /^([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\\.teamtailor\\.com$/i;\nconst raw = entry.api || entry.careers_url || '';\nlet parsed; try { parsed = new URL(raw); } catch { parsed = null; }\nconst explicit = entry.provider === 'teamtailor';\nif (parsed && !explicit && !TT_RE.test(parsed.hostname)) {\n  console.warn(`${entry.name}: branded host needs provider: teamtailor to be used`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["For any non-*.teamtailor.com host, always set provider: teamtailor.","Confirm the branded host actually serves /jobs.rss before relying on it.","Lint entries whose careers_url host is not *.teamtailor.com and lacks the provider opt-in."],"tags":["ssrf-guard","url-validation","hostname","config","teamtailor","portals-yml"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}