{"record":{"id":"5f6c85165ae48efe","repo":"santifer/career-ops","slug":"gem-url-must-use-https-url","errorCode":null,"errorMessage":"gem: URL must use HTTPS: ${url}","messagePattern":"gem: URL must use HTTPS: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"providers/gem.mjs","lineNumber":125,"sourceCode":"function buildJobDescriptionText(posting) {\n  const intro = htmlToText(posting?.jobPostSectionHtml?.introHtml);\n  const body = htmlToText(posting?.descriptionHtml);\n  const outro = htmlToText(posting?.jobPostSectionHtml?.outroHtml);\n  const compensation = htmlToText(posting?.compensationHtml);\n\n  const text = [intro, body, outro].filter(Boolean).join('\\n\\n');\n  return compensation ? [text, `Compensation: ${compensation}`].filter(Boolean).join('\\n\\n') : text;\n}\n\n/** @param {string} url */\nfunction assertGemUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`gem: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`gem: URL must use HTTPS: ${url}`);\n  if (!ALLOWED_GEM_HOSTS.has(parsed.hostname))\n    throw new Error(`gem: untrusted hostname \"${parsed.hostname}\" — must be one of: ${[...ALLOWED_GEM_HOSTS].join(', ')}`);\n  return url;\n}\n\n/** Resolve an explicitly pinned URL for Gem's documented REST job-board API. */\nfunction resolveRestApiUrl(entry) {\n  const raw = typeof entry.api === 'string' ? entry.api : '';\n  if (!raw) return null;\n  let parsed;\n  try {\n    parsed = new URL(raw);\n  } catch {\n    return null;\n  }\n  if (parsed.protocol !== 'https:' || parsed.hostname !== 'api.gem.com') return null;\n  if (!/^\\/job_board\\/v0\\/[^/?#]+\\/job_posts\\/?$/.test(parsed.pathname)) return null;\n  return parsed;","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/gem.mjs#L107-L143","documentation":"URL scheme guard in the GEM board provider (assertGemUrl): the parsed careers/board URL's protocol is not https:. The URL parsed successfully but uses http: or another scheme; the provider refuses to fetch over a non-HTTPS connection. The input at fault is the careers_url configured for the board.","triggerScenarios":"Passing 'http://boards.gem.com/...' or any ftp:/ws:/file: scheme URL into assertGemUrl, or configuring a board entry whose api/careers URL starts with http:// in portals.yml.","commonSituations":"Copying an HTTP link from an older config or docs snippet; a redirect target recorded as http://; internal staging boards configured with http for local testing and forgotten.","solutions":["Change the URL scheme from http:// to https:// in the config entry.","Verify the host actually serves HTTPS (test with curl -I https://...); most Gem boards do.","If this is a local/dev stub, use an HTTPS local endpoint or a test double instead of downgrading the scheme."],"exampleFix":"// before\nassertGemUrl('http://boards.gem.com/company');\n// after\nassertGemUrl('https://boards.gem.com/company');","handlingStrategy":"validation","validationCode":"if (!u.startsWith('https://')) throw new Error(`Gem URLs must be https://, got: ${u}`);","typeGuard":"const isHttpsUrl = (u) => { try { return new URL(u).protocol === 'https:'; } catch { return false; } };","tryCatchPattern":"try { assertGemUrl(url); } catch (e) { if (e.message.includes('must use HTTPS')) throw new Error(`Fix scheme for ${url}: use https://`); throw e; }","preventionTips":["Never configure http:// for external job boards","Grep configs for 'http://' on a schedule","Use HTTPS-everywhere defaults in templates","Test staging boards over HTTPS instead of downgrading the scheme"],"tags":["url","https","security","config"],"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"}