{"record":{"id":"3dd2853273bfb456","repo":"santifer/career-ops","slug":"flowxtra-url-must-use-https-url","errorCode":null,"errorMessage":"flowxtra: URL must use HTTPS: ${url}","messagePattern":"flowxtra: URL must use HTTPS: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"providers/flowxtra.mjs","lineNumber":33,"sourceCode":"//\n// Wire in via a `job_boards:` entry with `provider: flowxtra`.\n\nconst JOBS_ENDPOINT = 'https://app.flowxtra.com/api/central/jobs';\nconst TRUSTED_ENDPOINT_HOST = 'app.flowxtra.com';\nconst TRUSTED_APPLY_HOST = 'flowxtra.com';\nconst PER_PAGE = 100;\nconst DEFAULT_MAX_PAGES = 3;\nconst MAX_PAGES_CAP = 50;\n\n/** @param {string} url */\nfunction assertFlowxtraEndpointUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`flowxtra: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`flowxtra: URL must use HTTPS: ${url}`);\n  if (parsed.hostname !== TRUSTED_ENDPOINT_HOST) {\n    throw new Error(`flowxtra: untrusted hostname \"${parsed.hostname}\" — must be ${TRUSTED_ENDPOINT_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// NaN-safe Date.parse — `|| undefined` would also coerce a valid epoch 0.\nfunction toEpochMs(value) {\n  if (!value) return undefined;\n  const parsed = Date.parse(value);\n  return Number.isNaN(parsed) ? undefined : parsed;","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/flowxtra.mjs#L15-L51","documentation":"assertFlowxtraEndpointUrl in providers/flowxtra.mjs validates that the Flowxtra jobs endpoint URL is well-formed, uses HTTPS, and points at the trusted endpoint host before any request is made. This specific throw fires when the URL parses but its protocol is not 'https:'. The provider deliberately refuses plain HTTP to prevent credentials or job data from being sent unencrypted and to block protocol-based SSRF tricks.","triggerScenarios":"Passing a URL whose parsed.protocol is anything other than 'https:' — e.g. an entry configured with an http:// endpoint, a URL built with an empty/injected protocol, or a string like 'ftp://host/path' that still parses via new URL().","commonSituations":"A portals.yml careers entry pasted from an internal intranet using http://; a config typo dropping the 's'; test fixtures substituting http://localhost endpoints that the assertion rejects in production code paths.","solutions":["Change the endpoint URL to start with https:// (e.g. https://flowxtra.example.com/...).","If the endpoint genuinely only serves HTTP, put an HTTPS-terminating proxy in front and point the entry at the proxy host.","Check the config source of the URL for template/variable mistakes that drop or corrupt the scheme.","If a local test endpoint is the cause, use an https-capable local setup (self-signed cert) rather than downgrading the assertion."],"exampleFix":"// before\nendpoint: \"http://flowxtra.example.com/api/jobs\"\n// after\nendpoint: \"https://flowxtra.example.com/api/jobs\"","handlingStrategy":"validation","validationCode":"function isHttpsUrl(u) {\n  try { return new URL(u).protocol === 'https:'; } catch { return false; }\n}\nif (!isHttpsUrl(endpoint)) throw new Error(`config: endpoint must be https: ${endpoint}`);","typeGuard":"const isHttpsUrl = (u) => { try { return new URL(u).protocol === 'https:'; } catch { return false; } };","tryCatchPattern":"try {\n  await provider.fetch(entry);\n} catch (e) {\n  if (String(e.message).startsWith('flowxtra: URL must use HTTPS')) {\n    console.error(`Fix portals.yml entry \"${entry.name}\": use https://`);\n    return;\n  }\n  throw e;\n}","preventionTips":["Always store endpoint URLs with the full https:// scheme in portals.yml.","Validate all careers_url values at config-load time with a URL parse + scheme check.","Add a lint step that scans portals.yml for http:// endpoints."],"tags":["security","url-validation","config","ssrf-guard"],"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"}