{"record":{"id":"5cb62aa1bcde2be3","repo":"santifer/career-ops","slug":"jobstreet-url-must-use-https-url","errorCode":null,"errorMessage":"jobstreet: URL must use HTTPS: ${url}","messagePattern":"jobstreet: URL must use HTTPS: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/jobstreet.mjs","lineNumber":58,"sourceCode":"  'my.jobstreet.com',\n  'www.seek.com.au',\n  'www.seek.co.nz',\n]);\n\n// v5 API paths (the client-side JS on jobstreet uses these relative paths\n// resolved against the current origin). We keep the allowlist for SSRF\n// protection on the base URL, then build the v5 search path from it.\nconst V5_SEARCH_PATH = '/api/jobsearch/v5/search';\n\n/** @param {string} url */\nfunction assertJobstreetUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`jobstreet: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`jobstreet: URL must use HTTPS: ${url}`);\n  if (!ALLOWED_JOBSTREET_HOSTS.has(parsed.hostname))\n    throw new Error(`jobstreet: untrusted hostname \"${parsed.hostname}\" — must be one of: ${[...ALLOWED_JOBSTREET_HOSTS].join(', ')}`);\n  return url;\n}\n\n/**\n * Derive the origin from the API hostname.\n * e.g. id.jobstreet.com → https://id.jobstreet.com\n * @param {string} apiUrl\n * @returns {string}\n */\nfunction deriveOrigin(apiUrl) {\n  try {\n    const parsed = new URL(apiUrl);\n    return `${parsed.protocol}//${parsed.hostname}`;\n  } catch {\n    return 'https://id.jobstreet.com';\n  }","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/providers/jobstreet.mjs#L40-L76","documentation":"Thrown by assertJobstreetUrl when entry.api parses but its protocol is not https:. The assert runs against entry.api || DEFAULT_API, so this is reachable whenever a user sets api: to an http:// URL.","triggerScenarios":"A portal entry with provider: jobstreet and api: set to an http:// URL (e.g. a captured request or a regional mirror without TLS). DEFAULT_API is https so omitting api: never triggers this.","commonSituations":"Copying an http URL from a tool that downgraded the scheme; pointing at an internal/http-only proxy mirror during development; a typo dropping the 's' in https://.","solutions":["Set api: to the https:// form of the endpoint.","If the regional endpoint you need is genuinely http-only (unusual for Jobstreet), that is unsupported — use an https mirror or omit api:.","Add a config lint that flags non-https api: values across all providers."],"exampleFix":"# before\n- name: Jobstreet ID\n  provider: jobstreet\n  api: http://id.jobstreet.com/api/jobsearch/v5/search\n\n# after\n- name: Jobstreet ID\n  provider: jobstreet\n  api: https://id.jobstreet.com/api/jobsearch/v5/search","handlingStrategy":"validation","validationCode":"if (typeof entry.api === 'string') {\n  const u = new URL(entry.api); // throws on malformed — see error 216\n  if (u.protocol !== 'https:') {\n    throw new Error(`jobstreet: entry.api must use https: ${entry.api}`);\n  }\n}","typeGuard":"/** True only for an https: absolute URL. */\nfunction isHttpsUrl(value) {\n  try { return new URL(value).protocol === 'https:'; } catch { return false; }\n}","tryCatchPattern":"try {\n  return await jobstreetProvider.fetch(entry, ctx);\n} catch (err) {\n  if (/URL must use HTTPS/.test(err.message)) {\n    console.error(`config: ${entry.name} — ${err.message}`);\n  } else {\n    throw err;\n  }\n}","preventionTips":["Always set entry.api with the https:// scheme.","Run a config lint that flags any non-https api: across providers.","If a regional endpoint is http-only, that is unsupported — use an https mirror or omit api:."],"tags":["config","url-validation","tls","jobstreet","ssrf"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}