{"record":{"id":"df2f2edd19c67ad8","repo":"santifer/career-ops","slug":"getonbrd-url-must-use-https-url","errorCode":null,"errorMessage":"getonbrd: URL must use HTTPS: ${url}","messagePattern":"getonbrd: URL must use HTTPS: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"providers/getonbrd.mjs","lineNumber":83,"sourceCode":"    throw new Error('getonbrd: `categories` is empty — omit it to use the \"programming\" default');\n  }\n  if (out.length > MAX_CATEGORIES) {\n    throw new Error(\n      `getonbrd: ${out.length} categories configured — cap is ${MAX_CATEGORIES} (each one costs up to max_pages requests)`,\n    );\n  }\n  return out;\n}\n\n/** @param {string} url */\nfunction assertGetonbrdUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`getonbrd: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`getonbrd: URL must use HTTPS: ${url}`);\n  if (parsed.hostname !== TRUSTED_HOST) {\n    throw new Error(`getonbrd: untrusted hostname \"${parsed.hostname}\" — must be ${TRUSTED_HOST}`);\n  }\n  return url;\n}\n\n/** Resolve the page cap: a positive integer `max_pages` on the entry, capped. */\nfunction resolveMaxPages(entry) {\n  const v = entry?.max_pages;\n  if (Number.isInteger(v) && v > 0) return Math.min(v, MAX_PAGES_CAP);\n  return DEFAULT_MAX_PAGES;\n}\n\n/**\n * Normalize a single Get on Board job (JSON:API resource). Exported for tests.\n *\n * Field mapping → the normalized Job shape:\n *   - title:    `attributes.title`, trimmed (items without one are dropped).","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/getonbrd.mjs#L65-L101","documentation":"assertGetonbrdUrl requires every URL it accepts to use the https: protocol. After a successful URL parse, if parsed.protocol is anything else (http:, ftp:, file:, etc.), the function throws this error. This enforces transport security for all Get on Board requests made by the provider.","triggerScenarios":"A portals.yml entry passes a URL that parses fine but uses a non-HTTPS scheme — typically 'http://www.getonbrd.com/...' — and the provider runs it through assertGetonbrdUrl.","commonSituations":"Copying an old http:// link from bookmarks or logs; writing http by habit when hand-editing portals.yml; a redirect-generation script producing http URLs.","solutions":["Change the URL scheme to https:// in the portals.yml entry","Check for scheme variables or env interpolation in config generation that may emit http","Note that the hostname check runs after this one, so fix the scheme first, then re-run to surface any hostname issue"],"exampleFix":"// before\ncareers_url: http://www.getonbrd.com/api/v0/categories/programming/jobs\n// after\ncareers_url: https://www.getonbrd.com/api/v0/categories/programming/jobs","handlingStrategy":"validation","validationCode":"const u = new URL(entry.careers_url);\nif (u.protocol !== 'https:') throw new Error(`${entry.name}: careers_url must use https:// (got ${u.protocol})`);","typeGuard":"function isHttpsUrl(v) { try { return new URL(v).protocol === 'https:'; } catch { return false; } }","tryCatchPattern":"try {\n  assertGetonbrdUrl(url);\n} catch (e) {\n  if (String(e.message).includes('must use HTTPS')) {\n    url = url.replace(/^http:/, 'https:'); // auto-upgrade then retry\n  } else throw e;\n}","preventionTips":["Default to https:// when writing any careers_url or api value","Never rely on the provider to upgrade http to https","Grep config for 'http://' (without s) before committing changes"],"tags":["https","url-validation","security","getonbrd"],"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"}