{"record":{"id":"e2d4580ad10a6ef4","repo":"santifer/career-ops","slug":"pinpoint-invalid-url-url","errorCode":null,"errorMessage":"pinpoint: invalid URL: ${url}","messagePattern":"pinpoint: invalid URL: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/pinpoint.mjs","lineNumber":30,"sourceCode":"//\n// Per-tenant subdomains are the variable part — SSRF defence uses a regex\n// match on `<safe-slug>.pinpointhq.com` rather than a static allowlist, the\n// same approach as the recruitee provider.\n\n// The tenant label must be a valid DNS label: it may contain hyphens but must\n// not start or end with one (so `acme-.pinpointhq.com` is rejected). The\n// optional trailing group keeps single-character labels (e.g. `a.pinpointhq.com`)\n// valid. detect() and fetch() both route through this constant via\n// resolveApiUrl()/assertPinpointUrl(), so the stricter check applies everywhere.\nconst PINPOINT_HOST_RE = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.pinpointhq\\.com$/;\n\n/** @param {string} url */\nfunction assertPinpointUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`pinpoint: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`pinpoint: URL must use HTTPS: ${url}`);\n  if (!PINPOINT_HOST_RE.test(parsed.hostname)) {\n    throw new Error(`pinpoint: untrusted hostname \"${parsed.hostname}\" — must match <slug>.pinpointhq.com`);\n  }\n  return url;\n}\n\nfunction resolveApiUrl(entry) {\n  const raw = typeof entry.careers_url === 'string' ? entry.careers_url : '';\n  if (!raw) return null;\n  let parsed;\n  try {\n    parsed = new URL(raw);\n  } catch {\n    return null;\n  }\n  if (parsed.protocol !== 'https:') return null;","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/providers/pinpoint.mjs#L12-L48","documentation":"Thrown by pinpoint's assertPinpointUrl() when new URL(url) throws — the URL is syntactically invalid. First of three SSRF gates (valid URL → HTTPS → trusted hostname regex) for Pinpoint ATS boards. The hostname regex PINPOINT_HOST_RE enforces the <slug>.pinpointhq.com pattern, requiring a slug that starts and ends with alphanumeric (allowing internal hyphens).","triggerScenarios":"Called with an unparseable URL: undefined, empty string, spaces, or a schemeless path. The guard is called from resolveApiUrl() and fetch(), both routing through assertPinpointUrl. A typical trigger is a careers_url with a typo or missing scheme that reaches assertPinpointUrl past detect()'s null-return guard.","commonSituations":"Portals.yml pinpoint entry with careers_url missing or malformed. A programmatic entry without the URL field. A URL copied without the https:// scheme. Testing with a relative fixture path.","solutions":["Log the url argument to assertPinpointUrl to identify the malformed value.","Set careers_url in the pinpoint portals.yml entry to https://<slug>.pinpointhq.com.","If calling resolveApiUrl() directly, validate the entry has a non-empty careers_url first."],"exampleFix":"// before — missing scheme\ncareers_url: 'acme.pinpointhq.com'\n\n// after\ncareers_url: 'https://acme.pinpointhq.com'","handlingStrategy":"validation","validationCode":"/** Validate URL string is parseable before passing to assertPinpointUrl. */\nfunction isValidUrlString(url) {\n  return typeof url === 'string'\n    && url.length > 0\n    && (() => { try { new URL(url); return true; } catch { return false; } })();\n}\n\nif (!isValidUrlString(entry.careers_url)) {\n  console.warn(`pinpoint entry ${entry.name} has invalid URL`);\n  continue;\n}","typeGuard":"/** @param {unknown} url @returns {url is string} */\nfunction isParseableUrl(url) {\n  if (typeof url !== 'string' || !url) return false;\n  try { new URL(url); return true; } catch { return false; }\n}","tryCatchPattern":"try {\n  await pinpointProvider.fetch(entry, ctx);\n} catch (err) {\n  if (String(err.message).startsWith('pinpoint: invalid URL')) {\n    console.warn(`skipping pinpoint entry ${entry.name}: malformed URL`);\n    continue;\n  }\n  throw err;\n}","preventionTips":["Validate URL fields in portals.yml at config-load time.","Ensure Pinpoint URLs include https:// and follow the <slug>.pinpointhq.com pattern.","Call resolveApiUrl(entry) or detect(entry) before fetch() and skip entries returning null."],"tags":["url-validation","ssrf-guard","pinpoint","config"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}