{"record":{"id":"bf0b7fd41d02cc3b","repo":"santifer/career-ops","slug":"justjoin-url-must-use-https-url","errorCode":null,"errorMessage":"justjoin: URL must use HTTPS: ${url}","messagePattern":"justjoin: URL must use HTTPS: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/justjoin.mjs","lineNumber":21,"sourceCode":"\n// JustJoin.it provider — hits the current candidate offers API.\n// Browser URLs under https://justjoin.it/job-offers/... are accepted for\n// detection, but fetches use https://justjoin.it/api/candidate-api/offers.\n\nconst ALLOWED_HOSTS = new Set(['justjoin.it']);\nconst API_BASE = 'https://justjoin.it/api/candidate-api/offers';\nconst JOB_BASE = 'https://justjoin.it/job-offer/';\nconst PAGE_SIZE = 100;\nconst MAX_PAGES = 50;\n\nfunction assertJustJoinUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`justjoin: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`justjoin: URL must use HTTPS: ${url}`);\n  if (!ALLOWED_HOSTS.has(parsed.hostname)) {\n    throw new Error(`justjoin: untrusted hostname \"${parsed.hostname}\" — must be justjoin.it`);\n  }\n  if (!parsed.pathname.startsWith('/job-offers') && parsed.pathname !== '/api/candidate-api/offers') {\n    throw new Error(`justjoin: URL path must be /job-offers or /api/candidate-api/offers: ${url}`);\n  }\n  return parsed;\n}\n\nfunction detectUrl(entry) {\n  const url = entry.api || entry.careers_url || '';\n  if (typeof url !== 'string' || !url.trim()) return null;\n  try {\n    const parsed = assertJustJoinUrl(url);\n    return { url: parsed.href };\n  } catch {\n    return null;\n  }","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/justjoin.mjs#L3-L39","documentation":"This error is thrown by assertJustJoinUrl in providers/justjoin.mjs when a configured justjoin.it URL uses a protocol other than https:. The provider only accepts HTTPS because it talks to justjoin.it's candidate API with credentials-free requests and forbids downgrade/redirect tricks (fetch uses redirect: 'error'). It is an input-validation guard on portals.yml entries before any network call is made.","triggerScenarios":"Passing a URL string whose parsed.protocol is 'http:' (or ftp:, etc.) to assertJustJoinUrl — directly, or indirectly via buildApiUrl on entry.api, or via detectUrl during detect()/fetch(). For example entry.api: 'http://justjoin.it/api/candidate-api/offers' in portals.yml.","commonSituations":"A portals.yml careers_url or api value hand-typed as http:// instead of https://; a URL copied from an old blog post or HTTP mirror; a staging/proxy URL on http; string interpolation dropping the 's' when assembling the API base.","solutions":["Change the URL in portals.yml (careers_url or api) to use https:// — e.g. https://justjoin.it/api/candidate-api/offers","If the URL comes from an env var or script, normalize it before passing: ensure it starts with 'https://'","Verify with `new URL(url).protocol === 'https:'` before calling the provider's detect/fetch"],"exampleFix":"// before (portals.yml)\ncareers_url: http://justjoin.it/job-offers\n// after\ncareers_url: https://justjoin.it/job-offers","handlingStrategy":"validation","validationCode":"function isHttpsUrl(url) {\n  try { return new URL(url).protocol === 'https:'; } catch { return false; }\n}\nif (!isHttpsUrl(entry.api || entry.careers_url || '')) throw new Error('justjoin URL must be HTTPS');","typeGuard":"const isHttpsUrl = (u) => { try { return new URL(u).protocol === 'https:'; } catch { return false; } };","tryCatchPattern":"try {\n  await provider.fetch(entry, ctx);\n} catch (e) {\n  if (e.message.startsWith('justjoin: URL must use HTTPS')) {\n    console.error(`Fix portals.yml entry: ${e.message}`);\n  } else throw e;\n}","preventionTips":["Always write https:// in portals.yml URLs","Add a config lint step that checks new URL(u).protocol === 'https:' for all careers_url/api values","Never assemble provider URLs via string concatenation without validating the result"],"tags":["validation","url","https","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"}