santifer/career-ops · error · Error
jobstreet: untrusted hostname "${parsed.hostname}" — must be
Error message
jobstreet: untrusted hostname "${parsed.hostname}" — must be one of: ${[...ALLOWED_JOBSTREET_HOSTS].join(', ')} What it means
Thrown by assertJobstreetUrl when entry.api's hostname is not in ALLOWED_JOBSTREET_HOSTS (id.jobstreet.com, www.jobstreet.com, www.jobstreet.co.id, jobstreet.com, jobstreet.co.id, sg.jobstreet.com, my.jobstreet.com, www.seek.com.au, www.seek.co.nz). This is the core SSRF allowlist; it fires when a user points api: at a Jobstreet/SEEK regional host the provider does not yet allow.
Source
Thrown at providers/jobstreet.mjs:60
'www.seek.co.nz',
]);
// v5 API paths (the client-side JS on jobstreet uses these relative paths
// resolved against the current origin). We keep the allowlist for SSRF
// protection on the base URL, then build the v5 search path from it.
const V5_SEARCH_PATH = '/api/jobsearch/v5/search';
/** @param {string} url */
function assertJobstreetUrl(url) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`jobstreet: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`jobstreet: URL must use HTTPS: ${url}`);
if (!ALLOWED_JOBSTREET_HOSTS.has(parsed.hostname))
throw new Error(`jobstreet: untrusted hostname "${parsed.hostname}" — must be one of: ${[...ALLOWED_JOBSTREET_HOSTS].join(', ')}`);
return url;
}
/**
* Derive the origin from the API hostname.
* e.g. id.jobstreet.com → https://id.jobstreet.com
* @param {string} apiUrl
* @returns {string}
*/
function deriveOrigin(apiUrl) {
try {
const parsed = new URL(apiUrl);
return `${parsed.protocol}//${parsed.hostname}`;
} catch {
return 'https://id.jobstreet.com';
}
}
View on GitHub (pinned to 9b17a8ac97)
Solutions
- If the regional host is legitimate and trusted, add it to ALLOWED_JOBSTREET_HOSTS in providers/jobstreet.mjs (and add a regression test).
- Otherwise switch api: to one of the allowed hosts, or omit api: to use DEFAULT_API (id.jobstreet.com).
- Double-check the hostname spelling against the allowlist in the error message.
Example fix
// before — regional host not in the allowlist const ALLOWED_JOBSTREET_HOSTS = new Set(['id.jobstreet.com', /* ... */]); // after — add the Philippine market const ALLOWED_JOBSTREET_HOSTS = new Set(['id.jobstreet.com', 'ph.jobstreet.com', /* ... */]);
Defensive patterns
Strategy: validation
Validate before calling
import { readFileSync } from 'node:fs';
// Hard-code the same allowlist the provider uses, for a pre-scan lint.
const ALLOWED = new Set(['id.jobstreet.com','www.jobstreet.com','www.jobstreet.co.id','jobstreet.com','jobstreet.co.id','sg.jobstreet.com','my.jobstreet.com','www.seek.com.au','www.seek.co.nz']);
function assertJobstreetApiHost(apiUrl) {
const u = new URL(apiUrl);
if (!ALLOWED.has(u.hostname)) {
throw new Error(`jobstreet: ${u.hostname} not allowlisted — add it to ALLOWED_JOBSTREET_HOSTS or use a supported host`);
}
} Type guard
/** True for a URL whose host is in the Jobstreet/SEEK allowlist. */
function isJobstreetHostAllowed(url) {
try { return ALLOWED.has(new URL(url).hostname); } catch { return false; }
} Try / catch
try {
return await jobstreetProvider.fetch(entry, ctx);
} catch (err) {
if (/untrusted hostname/.test(err.message)) {
console.error(`config: ${entry.name} — ${err.message} (add the host to ALLOWED_JOBSTREET_HOSTS if legitimate)`);
} else {
throw err;
}
} Prevention
- When targeting a new Jobstreet market (PH/TH/HK), add its host to ALLOWED_JOBSTREET_HOSTS with a regression test.
- Remember the allowlist is explicit — *.jobstreet.com is NOT a blanket pass.
- If unsure, omit api: and rely on DEFAULT_API.
When it happens
Trigger: Setting api: to a regional host not in the allowlist — e.g. ph.jobstreet.com (Philippines), th.jobstreet.com (Thailand), hk.jobstreet.com (Hong Kong), or a SEEK host other than the two Australian/NZ ones; pointing api: at a wholly different (non-Jobstreet) host.
Common situations: Targeting a Jobstreet market the provider was not configured for (PH/TH/HK are common omissions); assuming any *.jobstreet.com subdomain is accepted (it is not — the set is explicit); a typo in the hostname.
Related errors
- jobstreet: invalid URL: ${url}
- jobstreet: URL must use HTTPS: ${url}
- arbeitnow: invalid URL: ${url}
- ashby: invalid URL: ${url}
- bamboohr: invalid URL: ${url}
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/f49a7c840832d032.
Report an issue: GitHub.