santifer/career-ops · error · Error

jobspresso: untrusted hostname "${parsed.hostname}" - must b

Error message

jobspresso: untrusted hostname "${parsed.hostname}" - must be ${TRUSTED_HOST}

What it means

Thrown by assertJobspressoUrl when the URL's hostname is not exactly jobspresso.co (strict equality, no subdomain tolerance). This is the core SSRF allowlist guard. The shipped provider only passes the hardcoded jobspresso.co constant, so it cannot fire unless the constant or the function's input changes.

Source

Thrown at providers/jobspresso.mjs:25

// approach as providers/personio.mjs rather than adding an XML dependency.
//
// Wire in via a `job_boards:` entry with `provider: jobspresso`.

const FEED_URL = 'https://jobspresso.co/?feed=job_feed';
const TRUSTED_HOST = 'jobspresso.co';

/** @param {string} url */
function assertJobspressoUrl(url) {
  let parsed;
  try {
    parsed = new URL(url);
  } catch {
    throw new Error(`jobspresso: invalid URL: ${url}`);
  }
  if (parsed.protocol !== "https:")
    throw new Error(`jobspresso: URL must use HTTPS: ${url}`);
  if (parsed.hostname !== TRUSTED_HOST) {
    throw new Error(
      `jobspresso: untrusted hostname "${parsed.hostname}" - must be ${TRUSTED_HOST}`,
    );
  }
  return url;
}

// NaN-safe Date.parse - `|| undefined` would also coerce a valid epoch 0.
function toEpochMs(value) {
  if (!value) return undefined;
  const parsed = Date.parse(value);
  return Number.isNaN(parsed) ? undefined : parsed;
}

/** @type {Provider} */
export default {
  id: "jobspresso",

  detect(entry) {

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Keep FEED_URL on the bare jobspresso.co host, or if a subdomain is genuinely required, update the assert to allow *.jobspresso.co.
  2. Never route untrusted entry input through assertJobspressoUrl without confirming the host.
  3. Document any allowlist change next to TRUSTED_HOST.

Example fix

// before (strict — rejects subdomains)
if (parsed.hostname !== TRUSTED_HOST) { throw ... }

// after (allow subdomains)
if (parsed.hostname !== TRUSTED_HOST && !parsed.hostname.endsWith('.' + TRUSTED_HOST)) { throw ... }
Defensive patterns

Strategy: validation

Validate before calling

const TRUSTED_HOST = 'jobspresso.co';
function isTrustedJobspressoHost(url) {
  const u = new URL(url);
  return u.hostname === TRUSTED_HOST; // assert is strict — subdomains NOT allowed
}

Type guard

/** True for a URL on exactly jobspresso.co (no subdomains, per the assert). */
function isJobspressoTrusted(url) {
  try {
    const u = new URL(url);
    return u.protocol === 'https:' && u.hostname === 'jobspresso.co';
  } catch { return false; }
}

Try / catch

try {
  return await jobspressoProvider.fetch(entry, ctx);
} catch (err) {
  if (/untrusted hostname/.test(err.message)) {
    console.error(`jobspresso: host not allowlisted — ${err.message}`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Editing FEED_URL to a different host (a CDN/mirror); reusing assertJobspressoUrl on entry input pointing at a subdomain like feed.jobspresso.co; an attempt to route the request off-host via a crafted URL.

Common situations: Switching to a subdomain endpoint and assuming the assert allows it; a fork adding a configurable host without widening the allowlist; malicious/typo entry input routed through the assert.

Related errors


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