santifer/career-ops · error · Error
jobbankca: untrusted hostname "${parsed.hostname}" — must be
Error message
jobbankca: untrusted hostname "${parsed.hostname}" — must be ${TRUSTED_HOST} What it means
assertJobBankUrl restricts requests to a single trusted host (TRUSTED_HOST, jobbank.canada.ca). Even a valid HTTPS URL pointing at another hostname is rejected with this error. This is an SSRF/supply-chain guard: the provider will never fetch from an arbitrary domain.
Source
Thrown at providers/jobbankca.mjs:102
}
/** @param {string} keyword @param {number} page */
export function buildFeedUrl(keyword, page) {
const params = new URLSearchParams({ searchstring: keyword, locationstring: '', page: String(page) });
return `${FEED_URL}?${params.toString()}`;
}
/** @param {string} url */
export function assertJobBankUrl(url) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`jobbankca: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`jobbankca: URL must use HTTPS: ${url}`);
if (parsed.hostname !== TRUSTED_HOST) {
throw new Error(`jobbankca: untrusted hostname "${parsed.hostname}" — must be ${TRUSTED_HOST}`);
}
return url;
}
// Resolve an Atom element's inner text: unwrap CDATA, else decode entities.
function extractText(inner) {
const cdata = inner.match(/^\s*<!\[CDATA\[([\s\S]*?)\]\]>\s*$/);
if (cdata) return cdata[1].trim();
return decodeEntities(inner).trim();
}
function tagText(block, tag) {
const m = block.match(new RegExp(`<${tag}\\b[^>]*>([\\s\\S]*?)</${tag}>`, 'i'));
return m ? extractText(m[1]) : '';
}
// <link rel="alternate" type="text/html" href="..."/> — an attribute here,
// not inner text (Atom), unlike an RSS <link>text</link>. An Atom entry mayView on GitHub (pinned to 1696bec4d0)
Solutions
- Point the URL back at the official trusted host (jobbank.canada.ca)
- For local testing, intercept at the HTTP client level (e.g. nock/undici mock) instead of changing the URL hostname
- If you genuinely need another domain, that belongs in a different provider module, not jobbankca
- Check the hostname for subtle typos or appended paths that make the host differ (host with port or subdomain won't match)
Example fix
// before const url = 'https://job-bank.canada.ca/atom.xml'; // after const url = 'https://jobbank.canada.ca/atom.xml';
Defensive patterns
Strategy: validation
Validate before calling
const TRUSTED = 'jobbank.canada.ca';
function isTrustedHost(u) { try { return new URL(u).hostname === TRUSTED; } catch { return false; } }
if (!isTrustedHost(cfg.url)) throw new Error(`config: host not allowed for jobbankca: ${cfg.url}`); Type guard
function isTrustedJobBankUrl(v) {
try { return new URL(v).hostname === 'jobbank.canada.ca'; } catch { return false; }
} Try / catch
try {
assertJobBankUrl(url);
} catch (e) {
if (e.message.includes('untrusted hostname')) {
console.error(`jobbankca only fetches from jobbank.canada.ca; got: ${url}`);
return null; // skip entry
}
throw e;
} Prevention
- Copy the official URL from jobbank.canada.ca rather than typing it
- For local dev, mock the HTTP client (nock/undici interceptor) instead of swapping hostnames
- Keep other domains in their own provider configs
- Watch for exact-match semantics: subdomains, ports, and www. prefixes all fail
When it happens
Trigger: Calling assertJobBankUrl with an https URL whose hostname is not the trusted host — e.g. a mirror domain, a typo like jobbank.canada.ca.evil.io, job-bank.canada.ca, or a test stub URL like https://localhost:3000/atom.xml.
Common situations: Using a proxy or mock server URL in dev, typo'd host in portals.yml, or intentionally pointing the provider at a local caching mirror expecting it to work.
Related errors
- getonbrd: untrusted hostname "${parsed.hostname}" — must be
- glints: untrusted hostname "${parsed.hostname}" — must be on
- jobspresso: untrusted hostname "${parsed.hostname}" - must b
- jobstreet: invalid URL: ${url}
- 4dayweek: untrusted hostname "${parsed.hostname}" — must be
AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01).
Data as JSON: /api/errors/40a14a401389a61b.
Report an issue: GitHub.