{"record":{"id":"2ad962a92430a95f","repo":"santifer/career-ops","slug":"remotli-url-must-use-https-url","errorCode":null,"errorMessage":"remotli: URL must use HTTPS: ${url}","messagePattern":"remotli: URL must use HTTPS: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/remotli.mjs","lineNumber":241,"sourceCode":"\n  const postedAt = toEpochMs(job.publishedAt || job.createdAt);\n  if (postedAt !== undefined) out.postedAt = postedAt;\n\n  const salary = resolveSalary(job);\n  if (salary) out.salary = salary;\n\n  return out;\n}\n\n/** Guard the API URL: HTTPS + remotli.ch only. */\nfunction assertRemotliUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`remotli: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`remotli: URL must use HTTPS: ${url}`);\n  if (!HOST_RE.test(parsed.hostname))\n    throw new Error(`remotli: untrusted hostname \"${parsed.hostname}\" — must be remotli.ch`);\n  return url;\n}\n\n/** @type {Provider} */\nexport default {\n  id: 'remotli',\n\n  detect(entry) {\n    const raw = typeof entry.careers_url === 'string' ? entry.careers_url : '';\n    if (!raw) return null;\n    let parsed;\n    try {\n      parsed = new URL(raw);\n    } catch {\n      return null;\n    }","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/remotli.mjs#L223-L259","documentation":"remotli provider validates every URL before use via assertRemotliUrl. After confirming the string parses as a URL, it rejects any URL whose protocol is not https:. This is a security guard ensuring no plaintext HTTP traffic is sent to the job board and no non-HTTPS scheme (file:, data:, etc.) is smuggled in.","triggerScenarios":"Passing a URL to the remotli provider whose parsed protocol !== 'https:' — e.g. an entry configured with http://remotli.ch/..., an ftp: or file: URL, or a URL built with a scheme-less string that still parsed (unlikely here) — triggers this throw.","commonSituations":"A portals.yml entry hand-edited to http://, a config value copied from an old docs page or an internal staging mirror served over HTTP, or code assembling the URL from an env var that lacks the scheme.","solutions":["Change the URL to use https:// (e.g. https://remotli.ch/...) in the provider entry or config.","Verify the scheme before calling the provider: normalize/upgrade http:// to https:// when building the URL.","Check env vars or config templates that supply the base URL and fix any that default to http."],"exampleFix":"// before\nurl = 'http://remotli.ch/api/jobs';\n// after\nurl = 'https://remotli.ch/api/jobs';","handlingStrategy":"validation","validationCode":"function isHttpsUrl(url) { try { return new URL(url).protocol === 'https:'; } catch { return false; } }\nif (!isHttpsUrl(entry.url)) throw new Error(`skip: non-HTTPS URL ${entry.url}`);","typeGuard":"const isHttpsUrl = (u) => { try { return new URL(u).protocol === 'https:'; } catch { return false; } };","tryCatchPattern":"try { await provider.fetch(entry, ctx); } catch (e) { if (String(e.message).includes('must use HTTPS')) { console.warn(`Fix config: ${entry.url}`); return null; } throw e; }","preventionTips":["Always write https:// in portal/provider config entries.","Add a lint/check step that scans config for http:// URLs.","Never build URLs by concatenating schemes from user input."],"tags":["url-validation","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"}