santifer/career-ops · error · Error

pinpoint: URL must use HTTPS: ${url}

Error message

pinpoint: URL must use HTTPS: ${url}

What it means

Thrown by pinpoint's assertPinpointUrl() when the URL parses but protocol is not 'https:'. Second SSRF gate: prevents plaintext HTTP fetches to Pinpoint ATS boards. The comment in source notes that detect() and fetch() both route through this constant, so the HTTPS requirement applies universally.

Source

Thrown at providers/pinpoint.mjs:32

// match on `<safe-slug>.pinpointhq.com` rather than a static allowlist, the
// same approach as the recruitee provider.

// The tenant label must be a valid DNS label: it may contain hyphens but must
// not start or end with one (so `acme-.pinpointhq.com` is rejected). The
// optional trailing group keeps single-character labels (e.g. `a.pinpointhq.com`)
// valid. detect() and fetch() both route through this constant via
// resolveApiUrl()/assertPinpointUrl(), so the stricter check applies everywhere.
const PINPOINT_HOST_RE = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.pinpointhq\.com$/;

/** @param {string} url */
function assertPinpointUrl(url) {
  let parsed;
  try {
    parsed = new URL(url);
  } catch {
    throw new Error(`pinpoint: invalid URL: ${url}`);
  }
  if (parsed.protocol !== 'https:') throw new Error(`pinpoint: URL must use HTTPS: ${url}`);
  if (!PINPOINT_HOST_RE.test(parsed.hostname)) {
    throw new Error(`pinpoint: untrusted hostname "${parsed.hostname}" — must match <slug>.pinpointhq.com`);
  }
  return url;
}

function resolveApiUrl(entry) {
  const raw = typeof entry.careers_url === 'string' ? entry.careers_url : '';
  if (!raw) return null;
  let parsed;
  try {
    parsed = new URL(raw);
  } catch {
    return null;
  }
  if (parsed.protocol !== 'https:') return null;
  if (!PINPOINT_HOST_RE.test(parsed.hostname)) return null;
  return `https://${parsed.hostname}/postings.json`;

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Update careers_url to use https:// in portals.yml.
  2. Use HTTPS for test mock servers.
  3. Audit config-generation scripts to enforce https:// as default.

Example fix

// before
careers_url: 'http://acme.pinpointhq.com'

// after
careers_url: 'https://acme.pinpointhq.com'
Defensive patterns

Strategy: validation

Validate before calling

/** Normalize Pinpoint URL to HTTPS. */
function ensureHttps(url) {
  if (typeof url !== 'string') return null;
  return url.replace(/^http:\/\//i, 'https://');
}

entry.careers_url = ensureHttps(entry.careers_url) || entry.careers_url;

Type guard

/** @param {string} url @returns {boolean} */
function isHttpsUrl(url) {
  try { return new URL(url).protocol === 'https:'; } catch { return false; }
}

Try / catch

try {
  await pinpointProvider.fetch(entry, ctx);
} catch (err) {
  if (String(err.message).includes('must use HTTPS')) {
    entry.careers_url = (entry.careers_url || '').replace(/^http:/i, 'https:');
    await pinpointProvider.fetch(entry, ctx);
  } else throw err;
}

Prevention

When it happens

Trigger: A valid URL with http: scheme: entry.careers_url prefixed http://, or a test fixture against http://localhost. The value reaches assertPinpointUrl via resolveApiUrl() or a direct call.

Common situations: Portals.yml authored with http://. Config tool defaulting to http. Local development against a non-TLS mock server.

Related errors


AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13). Data as JSON: /api/errors/e0b31f56556d82c3. Report an issue: GitHub.