{"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":"exception","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/9b17a8ac97b398a496b38e423ae24e433b43254f/providers/justjoin.mjs#L3-L39","documentation":"Thrown by assertJustJoinUrl() when the URL parses but its protocol is not https:. The justjoin.it provider pins all requests to HTTPS as an SSRF guard; an http:// URL (or any other scheme) is rejected before any network call. The check runs on both the configured api/careers_url and any URL built by buildApiUrl().","triggerScenarios":"An api or careers_url entry using http:// instead of https://; a constructed URL that inherited a non-https scheme from user input.","commonSituations":"A legacy or hand-edited entry that used http; a copy-paste from a source that stripped the scheme to http.","solutions":["Change the protocol in the api/careers_url field to https://.","Re-run the scan to confirm the guard no longer trips."],"exampleFix":"# before\nacme:\n  provider: justjoin\n  api: http://justjoin.it/api/candidate-api/offers\n\n# after\nacme:\n  provider: justjoin\n  api: https://justjoin.it/api/candidate-api/offers","handlingStrategy":"validation","validationCode":"function isHttps(url) {\n  try { return new URL(url).protocol === 'https:'; } catch { return false; }\n}","typeGuard":"/** @param {string} url @returns {boolean} */\nfunction isHttpsUrl(url) {\n  try { return new URL(url).protocol === 'https:'; } catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["Normalize all config URLs to https:// at load time.","Reject http:// entries in a config-lint pass.","Document the HTTPS-only policy for every provider."],"tags":["justjoin","ssrf-guard","url-validation","config"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}