santifer/career-ops · error · Error

himalayas: untrusted hostname

Error message

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

What it means

The himalayas provider validates every URL it will request against a single allowlisted hostname (TRUSTED_HOST, himalayas.app). assertHimalayasUrl parses the URL, requires HTTPS, and throws this error when the hostname does not exactly match the trusted host. This is an SSRF guard: the scanner must never be pointed at an arbitrary host.

Solutions

  1. Fix the URL so its hostname is exactly the TRUSTED_HOST (himalayas.app) and uses https://
  2. If you need to test against a mock server, override/stub TRUSTED_HOST in the test setup instead of passing a local URL
  3. Remove or disable the misconfigured portals.yml entry so scan.mjs does not attempt it

Example fix

// before
assertHimalayasUrl('https://api.himalayas.example.com/feed');
// after
assertHimalayasUrl('https://himalayas.app/feed');
Defensive patterns

Strategy: validation

Validate before calling

function isSafeHimalayasUrl(url, trusted = 'himalayas.app') {
  try {
    const u = new URL(url);
    return u.protocol === 'https:' && u.hostname === trusted;
  } catch { return false; }
}
// call before scan: if (!isSafeHimalayasUrl(entry.careers_url)) skip entry;

Type guard

const isStringUrl = (v) => typeof v === 'string' && v.length > 0;
const isHttps = (u) => u.protocol === 'https:';
const isTrustedHost = (u, host) => u.hostname === host;

Try / catch

try {
  assertHimalayasUrl(url);
} catch (err) {
  if (String(err.message).startsWith('himalayas:')) {
    console.warn(`skipping misconfigured himalayas entry: ${err.message}`);
    return null;
  }
  throw err;
}

Prevention

When it happens

Trigger: assertHimalayasUrl is called with a URL whose parsed hostname differs from TRUSTED_HOST — e.g. a portals.yml entry pointing at a mirror domain, a staging host like himalayas.app.evil.com, or a http->https proxy domain.

Common situations: A developer adds a custom portals.yml entry with a wrong/typo'd careers_url; a test substitutes a mock server URL (localhost:3000) without overriding TRUSTED_HOST; an environment variable or config change rewrites the feed host.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16). Data as JSON: /api/errors/bcd95b182d24389c. Report an issue: GitHub.

Appendix: source

Thrown at providers/himalayas.mjs:24

// full feed is fetched so scan.mjs's title_filter / location_filter can do
// the local gating consistently with other zero-token board providers.
//
// Wire in via a `job_boards:` entry with `provider: himalayas`.

const FEED_URL = 'https://himalayas.app/jobs/api?limit=50';
const TRUSTED_HOST = 'himalayas.app';

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

function cleanText(value) {
  return typeof value === 'string' ? value.trim() : '';
}

function cleanHimalayasUrl(value) {
  const raw = cleanText(value);
  if (!raw) return '';
  try {
    const parsed = new URL(raw);
    const host = parsed.hostname.toLowerCase();
    const trusted = host === TRUSTED_HOST || host.endsWith(`.${TRUSTED_HOST}`);
    return parsed.protocol === 'https:' && trusted ? parsed.href : '';
  } catch {
    return '';

View on GitHub (pinned to aac998c7ed)