santifer/career-ops · error · Error
workable: untrusted hostname
Error message
workable: untrusted hostname "${parsed.hostname}" — must be one of: ${[...ALLOWED_WORKABLE_HOSTS].join(', ')} What it means
assertWorkableUrl enforces a hostname allowlist (ALLOWED_WORKABLE_HOSTS) after the HTTPS check. Any URL whose parsed hostname is not in that set is rejected to prevent SSRF — a configured or derived URL pointing at an arbitrary server is refused before any fetch happens.
Solutions
- Point the URL at an allowlisted Workable host (see the list in the message)
- Fix typos or embedded extra domains in the hostname
- If a new legitimate Workable host is genuinely needed, add it to ALLOWED_WORKABLE_HOSTS in providers/workable.mjs after verifying it
Example fix
// before
assertWorkableUrl('https://jobs.acme.com/widget?account=acme');
// after
assertWorkableUrl('https://apply.workable.com/acme/widget'); Defensive patterns
Strategy: validation
Validate before calling
const ALLOWED = ['apply.workable.com','workable.com'];
function isAllowedWorkableHost(url) { try { return ALLOWED.includes(new URL(url).hostname); } catch { return false; } } Type guard
const isAllowedWorkableHost = (u) => { try { return ['apply.workable.com','workable.com'].includes(new URL(u).hostname); } catch { return false; } }; Try / catch
try { return await workableProvider.fetch(entry, ctx); } catch (e) { if (e.message.includes('untrusted hostname')) { console.warn(`Entry ${entry.name} points at a non-Workable host; check careers_url`); return []; } throw e; } Prevention
- Only configure careers URLs on official Workable board domains
- Watch for lookalike hosts (extra suffixes) when copying URLs
- Review new portals.yml entries against the allowlist before enabling them
When it happens
Trigger: assertWorkableUrl called with a parsed hostname outside ALLOWED_WORKABLE_HOSTS, e.g. 'https://evil.example.com/acme' or a typo'd host like 'apply.workable.com.evil.io'.
Common situations: A careers_url pointing at a company's own site instead of its Workable board; a hostile/mistyped entry in portals.yml; a custom proxy host that is not allowlisted.
Related errors
- collage: untrusted hostname
- flowxtra: URL must use HTTPS
- senjob: untrusted hostname
- smartrecruiters: untrusted hostname
- solidjobs: untrusted hostname
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/67106249177ac59b.
Report an issue: GitHub.
Appendix: source
Thrown at providers/workable.mjs:118
// same host, so this process never needs more than one in-flight request to
// it at a time.
let workableQueue = Promise.resolve();
function serialized(fn) {
const result = workableQueue.then(fn, fn);
workableQueue = result.then(() => undefined, () => undefined);
return result;
}
function assertWorkableUrl(url) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`workable: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`workable: URL must use HTTPS: ${url}`);
if (!ALLOWED_WORKABLE_HOSTS.has(parsed.hostname)) {
throw new Error(`workable: untrusted hostname "${parsed.hostname}" — must be one of: ${[...ALLOWED_WORKABLE_HOSTS].join(', ')}`);
}
return url;
}
/**
* Extract the account slug from a tracked_companies entry's careers_url.
* @returns {string|null}
*/
export function resolveWorkableSlug(entry) {
const raw = entry && typeof entry.careers_url === 'string' ? entry.careers_url : '';
if (!raw) return null;
let parsed;
try {
parsed = new URL(raw);
} catch {
return null;
}
if (parsed.protocol !== 'https:') return null;View on GitHub (pinned to aac998c7ed)