{"record":{"id":"96703fc23c83815f","repo":"santifer/career-ops","slug":"teamtailor-url-must-use-https-url","errorCode":null,"errorMessage":"teamtailor: URL must use HTTPS: ${url}","messagePattern":"teamtailor: URL must use HTTPS: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/teamtailor.mjs","lineNumber":41,"sourceCode":"// never fetched.\n\nconst 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;","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/teamtailor.mjs#L23-L59","documentation":"After parsing succeeds, assertFeedUrl enforces HTTPS — teamtailor feeds are only fetched over TLS as a security baseline. Any http: URL, including protocol-relative or plain-host forms that parse with http as the default scheme, triggers this error.","triggerScenarios":"careers_url written as http://acme.teamtailor.com/jobs.rss; a URL without a scheme where new URL() defaulted the protocol to http:; a redirect bookkeeping entry still on an old http link.","commonSituations":"Legacy config from before HTTPS was mandatory; manually typed URL missing the 's'; internal proxy config reused for this provider.","solutions":["Change the scheme to https:// in portals.yml","Rerun the scan after fixing; do not bypass — the provider intentionally refuses plaintext"],"exampleFix":"// before\ncareers_url: 'http://acme.teamtailor.com/jobs.rss'\n// after\ncareers_url: 'https://acme.teamtailor.com/jobs.rss'","handlingStrategy":"validation","validationCode":"if (new URL(entry.careers_url).protocol !== 'https:') {\n  throw new Error(`${entry.name}: careers_url must use https://`);\n}","typeGuard":"function isHttps(u) { try { return new URL(u).protocol === 'https:'; } catch { return false; } }","tryCatchPattern":"try {\n  offers = await provider.fetch(entry, ctx);\n} catch (e) {\n  if (e.message.startsWith('teamtailor: URL must use HTTPS')) {\n    console.warn(`${entry.name}: switch careers_url to https://`);\n    return [];\n  }\n  throw e;\n}","preventionTips":["Never write http:// in careers_url fields","Add an HTTPS lint rule to config validation","Copy URLs over TLS from the browser"],"tags":["url-validation","https","config"],"backgroundTag":"invalid-url-format","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"}