{"record":{"id":"bf6bdebc2c61dd9f","repo":"santifer/career-ops","slug":"jobstreet-invalid-url-url","errorCode":null,"errorMessage":"jobstreet: invalid URL: ${url}","messagePattern":"jobstreet: invalid URL: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/jobstreet.mjs","lineNumber":56,"sourceCode":"  'jobstreet.co.id',\n  'sg.jobstreet.com',\n  '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 {","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/providers/jobstreet.mjs#L38-L74","documentation":"Thrown by assertJobstreetUrl when new URL(url) throws — the supplied api URL is not a parseable absolute URL. Unlike the himalayas/jobspresso guards, the jobstreet assert runs against entry.api || DEFAULT_API, so it IS reachable from the public contract whenever entry.api is set to a malformed string.","triggerScenarios":"A portal entry with provider: jobstreet and api: set to a malformed string — missing scheme (e.g. 'id.jobstreet.com/api/jobsearch/v5/search'), stray whitespace/characters, or a relative path. When entry.api is absent the provider uses the well-formed DEFAULT_API and this never fires.","commonSituations":"Omitting the https:// scheme when setting api:; a copy-paste that included surrounding quotes or whitespace; a templating step that produced a relative URL; YAML treating a value with special characters oddly.","solutions":["Set api: to a full absolute URL including the https:// scheme, e.g. https://id.jobstreet.com/api/jobsearch/v5/search.","If you do not need a custom endpoint, remove api: and let the provider use DEFAULT_API.","Trim whitespace and confirm new URL(entry.api) does not throw before running the scan."],"exampleFix":"# before\n- name: Jobstreet ID\n  provider: jobstreet\n  api: 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":"// Validate a user-supplied entry.api before the provider's assert throws.\nif (typeof entry.api === 'string') {\n  try { new URL(entry.api); }\n  catch { throw new Error(`jobstreet: entry.api is not a valid absolute URL: ${entry.api}`); }\n}","typeGuard":"/** True for a parseable absolute URL string. */\nfunction isAbsoluteUrl(value) {\n  if (typeof value !== 'string' || !value.trim()) return false;\n  try { new URL(value); return true; } catch { return false; }\n}","tryCatchPattern":"try {\n  return await jobstreetProvider.fetch(entry, ctx);\n} catch (err) {\n  if (/jobstreet: invalid URL/.test(err.message)) {\n    console.error(`config: ${entry.name} — entry.api is malformed: ${err.message}`);\n  } else {\n    throw err;\n  }\n}","preventionTips":["Always include the https:// scheme when setting entry.api; omit api: to use the well-formed default.","Trim whitespace from YAML values; quote URLs containing special characters.","Lint all provider: jobstreet entries for a parseable api: before a scan."],"tags":["config","url-validation","jobstreet","ssrf"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}