santifer/career-ops · error · Error

consider: ${entry.name} needs an https careers_url on a publ

Error message

consider: ${entry.name} needs an https careers_url on a public host

What it means

Thrown by the consider provider's fetch() when resolveOrigin(entry) returns null. resolveOrigin parses entry.careers_url and requires https, plus a public, non-internal hostname: it rejects IP literals (IPv4/IPv6), localhost, .local/.internal suffixes, and single-label hosts. Consider boards are always real registrable domains (jobs.founderful.com, etc.), so the guard both validates config and prevents an SSRF via a crafted careers_url aiming the POST at a private/metadata host.

Source

Thrown at providers/consider.mjs:92

  }
  if (Array.isArray(job.normalizedLocations) && job.normalizedLocations.length) {
    return job.normalizedLocations.map(l => l?.label || l?.value).filter(Boolean).join(', ');
  }
  return job.remote ? 'Remote' : '';
}

/** @type {Provider} */
export default {
  id: 'consider',

  detect(entry) {
    const origin = resolveOrigin(entry);
    return entry.consider_board && origin ? { url: origin + ENDPOINT_PATH } : null;
  },

  async fetch(entry, ctx) {
    const origin = resolveOrigin(entry);
    if (!origin) throw new Error(`consider: ${entry.name} needs an https careers_url on a public host`);
    if (!entry.consider_board) throw new Error(`consider: ${entry.name} needs a 'consider_board' id in portals.yml`);
    const size = Number.isInteger(entry.consider_size) && entry.consider_size > 0 ? entry.consider_size : DEFAULT_SIZE;

    const json = await ctx.fetchJson(origin + ENDPOINT_PATH, {
      method: 'POST',
      // redirect:'error' so a 3xx from the (config-driven) board host can't be
      // followed to a private/metadata IP — the host guard above pins the first hop.
      redirect: 'error',
      headers: { 'content-type': 'application/json', accept: 'application/json', referer: origin + '/jobs' },
      body: JSON.stringify({
        meta: { size },
        board: { id: String(entry.consider_board), isParent: true },
        query: { promoteFeatured: true },
      }),
    });

    const jobs = Array.isArray(json?.jobs) ? json.jobs : [];
    return jobs

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Set careers_url to the board's public https URL, e.g. https://jobs.founderful.com/jobs.
  2. If you need a staging board, host it on a real public domain — the guard deliberately rejects internal hosts.
  3. Gate with provider.detect(entry) (returns null when consider_board is missing OR origin fails) before calling fetch().

Example fix

# before — internal host, rejected by SSRF guard
- name: Founderful
  provider: consider
  consider_board: wingman
  careers_url: https://founderful.internal/jobs

# after — public https host
- name: Founderful
  provider: consider
  consider_board: wingman
  careers_url: https://jobs.founderful.com/jobs
Defensive patterns

Strategy: validation

Validate before calling

import consider from './providers/consider.mjs';
// detect() requires BOTH a public https origin AND a consider_board id.
if (!consider.detect(entry)) {
  // origin failed (this error) OR consider_board missing — check careers_url first
}

Type guard

/** True when careers_url is a public https host (not IP/localhost/internal). */
function hasPublicHttpsOrigin(entry) {
  const raw = typeof entry?.careers_url === 'string' ? entry.careers_url : '';
  if (!raw) return false;
  let host;
  try { host = new URL(raw).hostname.toLowerCase(); } catch { return false; }
  if (new URL(raw).protocol !== 'https:') return false;
  if (host.endsWith('.')) host = host.slice(0, -1);
  if (host.startsWith('[') || host.includes(':')) return false;
  if (/^\d{1,3}(\.\d{1,3}){3}$/.test(host)) return false;
  if (host === 'localhost' || host === 'localhost.localdomain') return false;
  if (host.endsWith('.local') || host.endsWith('.internal')) return false;
  return host.includes('.');
}

Try / catch

try { await consider.fetch(entry, ctx); }
catch (e) {
  if (/^consider: .* needs an https careers_url/.test(e.message)) {
    // SSRF/config guard — fix careers_url to a public https host; do not retry as-is
  } else throw e;
}

Prevention

When it happens

Trigger: entry.careers_url is missing, unparseable, non-https, an IP literal, 'localhost', a .local/.internal host, or a single-label (no dot) host. Note this is checked before consider_board, so a missing consider_board surfaces as a different error (line 93) once the origin resolves.

Common situations: Omitting careers_url; using an http:// URL; pointing at an internal staging host (*.internal) that the SSRF guard intentionally blocks; a malformed URL pasted from a rich-text source.

Related errors


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