{"record":{"id":"1d07f3fa4bacb17b","repo":"santifer/career-ops","slug":"comeet-url-path-must-be-the-careers-api-endpoint","errorCode":null,"errorMessage":"comeet: URL path must be the careers-api endpoint: ${redactToken(url)}","messagePattern":"comeet: URL path must be the careers-api endpoint: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"providers/comeet.mjs","lineNumber":40,"sourceCode":"  } catch {\n    return false;\n  }\n  return parsed.protocol === 'https:' && parsed.hostname === COMEET_API_HOST && parsed.pathname.startsWith('/careers-api/');\n}\n\n/** @param {string} url */\nfunction assertComeetUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`comeet: invalid URL: ${redactToken(url)}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`comeet: URL must use HTTPS: ${redactToken(url)}`);\n  if (parsed.hostname !== COMEET_API_HOST)\n    throw new Error(`comeet: untrusted hostname \"${parsed.hostname}\" — must be ${COMEET_API_HOST}`);\n  if (!parsed.pathname.startsWith('/careers-api/'))\n    throw new Error(`comeet: URL path must be the careers-api endpoint: ${redactToken(url)}`);\n  return url;\n}\n\n// Redact the per-tenant ?token= so neither the (informational, possibly-logged)\n// DetectHit url nor a thrown validation error carries the secret. Best-effort:\n// falls back to a regex strip when the value can't be parsed as a URL.\nfunction redactToken(url) {\n  try {\n    const parsed = new URL(url);\n    if (parsed.searchParams.has('token')) parsed.searchParams.set('token', 'REDACTED');\n    return parsed.href;\n  } catch {\n    return typeof url === 'string' ? url.replace(/([?&]token=)[^&#]*/gi, '$1REDACTED') : url;\n  }\n}\n\n/** @param {import('./_types.js').PortalEntry} entry */\nfunction resolveApiUrl(entry) {","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/comeet.mjs#L22-L58","documentation":"assertComeetUrl validates that a URL given to the Comeet provider points at Comeet's official API host over HTTPS and uses the /careers-api/ path prefix. The library throws this error when the URL passes the hostname and protocol checks but its pathname does not start with /careers-api/, meaning the provider would hit an unknown endpoint on www.comeet.co. It fails fast to prevent fetching from a path the parser does not understand.","triggerScenarios":"Calling fetch (or assertComeetUrl directly) with an entry whose api: URL is a Comeet careers *page* URL (e.g. https://www.comeet.com/company/jobs or /careers/...) or a bare host URL rather than the careers-api positions endpoint (e.g. https://www.comeet.co/careers-api/...).","commonSituations":"Copying the public jobs-page URL from a company's careers site instead of the underlying careers-api positions URL; hand-trimming the path when configuring portals.yml; a Comeet URL scheme change or migration leaving a stale path.","solutions":["Change the entry's api: value to the full careers-api positions URL, e.g. https://www.comeet.co/careers-api/v2.1/company/{company}/positions?token=...","Check the pathname begins with /careers-api/ (a quick new URL(api).pathname.startsWith('/careers-api/') check before configuring).","If you only have the public jobs page, locate or derive the careers-api endpoint Comeet's page calls and use that instead of the page URL.","Keep the per-tenant ?token= in the URL but be aware errors redact it — if the redacted log hides the path, inspect the entry config directly."],"exampleFix":"// before\napi: https://www.comeet.com/mycompany/jobs\n// after\napi: https://www.comeet.co/careers-api/v2.1/company/mycompany/positions?token=XXXX","handlingStrategy":"validation","validationCode":"function isComeetApiUrl(u) { try { const p = new URL(u); return p.protocol === 'https:' && p.hostname === 'www.comeet.co' && p.pathname.startsWith('/careers-api/'); } catch { return false; } }\nif (!isComeetApiUrl(entry.api)) throw new Error(`entry ${entry.name}: api must be a comeet /careers-api/ URL`);","typeGuard":"const isComeetApiUrl = (u) => { try { const p = new URL(u); return p.protocol === 'https:' && p.hostname === 'www.comeet.co' && p.pathname.startsWith('/careers-api/'); } catch { return false; } };","tryCatchPattern":null,"preventionTips":["Always copy the careers-api positions URL, never the public jobs page URL","Validate the URL shape in a pre-scan config linter before adding entries to portals.yml","Keep the ?token= in place — errors redact it, but fetch needs it","Compare against a known-working comeet entry when onboarding a new company"],"tags":["url-validation","config","comeet","ssrf-guard"],"backgroundTag":"invalid-url-format","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"}