santifer/career-ops · error · Error
4dayweek: untrusted hostname "${parsed.hostname}" — must be
Error message
4dayweek: untrusted hostname "${parsed.hostname}" — must be ${TRUSTED_HOST} What it means
The final allowlist check in assertFourDayUrl(): the URL is valid HTTPS, but its hostname is not the trusted host ('4dayweek.io', providers/4dayweek.mjs:21). This is the SSRF defense — it guarantees the provider only ever requests the vendor's own domain, so a crafted entry cannot make the scanner fetch internal or arbitrary hosts.
Source
Thrown at providers/4dayweek.mjs:57
}
} catch {
// Ignore malformed URLs; another provider may still claim the entry.
}
}
return null;
}
/** @param {string} url */
function assertFourDayUrl(url) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`4dayweek: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`4dayweek: URL must use HTTPS: ${url}`);
if (parsed.hostname !== TRUSTED_HOST) {
throw new Error(`4dayweek: untrusted hostname "${parsed.hostname}" — must be ${TRUSTED_HOST}`);
}
return url;
}
/** Resolve the page cap: a positive integer `max_pages` on the entry, capped. */
function resolveMaxPages(entry) {
const v = entry?.max_pages;
if (Number.isInteger(v) && v > 0) return Math.min(v, MAX_PAGES_CAP);
return DEFAULT_MAX_PAGES;
}
// NaN-safe: posted is epoch SECONDS → ms; anything non-finite yields undefined.
function toEpochMs(seconds) {
return Number.isFinite(seconds) ? seconds * 1000 : undefined;
}
/**
* Normalize a single 4 Day Week job. Exported for unit tests.View on GitHub (pinned to 1696bec4d0)
Solutions
- Use only URLs on https://4dayweek.io (the value of TRUSTED_HOST in providers/4dayweek.mjs:21).
- Normalize URLs before passing: strip tracking params and use the canonical 4dayweek.io job link.
- If you genuinely need another host, extend TRUSTED_HOST to an allowlist in the provider source — don't bypass the check at call sites.
- If URLs come from scraped pages, extract the job slug and rebuild the URL on the trusted host.
Example fix
// before
assertFourDayUrl('https://www.4dayweek.io/job/9'); // hostname mismatch
// after
assertFourDayUrl('https://4dayweek.io/job/9'); Defensive patterns
Strategy: validation
Validate before calling
const TRUSTED_HOST = '4dayweek.io';
function isTrustedUrl(url) {
try {
const u = new URL(url);
return u.protocol === 'https:' && u.hostname === TRUSTED_HOST;
} catch { return false; }
}
if (!isTrustedUrl(entry.url)) throw new Error(`Refusing non-trusted URL: ${entry.url}`); Type guard
function isTrusted4DayUrl(v) {
if (typeof v !== 'string') return false;
try {
const u = new URL(v);
return u.protocol === 'https:' && u.hostname === '4dayweek.io';
} catch { return false; }
} Try / catch
try {
provider.check(url);
} catch (err) {
if (err.message.includes('untrusted hostname')) {
console.warn(`Blocked SSRF-suspect URL (${err.message}); rebuilding on trusted host from slug ${slug}`);
return provider.check(`https://4dayweek.io/job/${encodeURIComponent(slug)}`);
}
throw err;
} Prevention
- Rebuild job URLs on the trusted host from slugs/IDs instead of trusting scraped hrefs.
- Never point provider entries at mirrors, proxies, or www-variants of the trusted domain.
- Keep SSRF allowlist checks intact — do not bypass them at call sites or in tests with mocks that skip validation.
- Log-and-drop untrusted URLs during scraping rather than feeding them to the provider.
When it happens
Trigger: Passing https:// URLs with a different hostname — mirrors, CDN hosts like www.4dayweek.io if it's not the constant, lookalike domains, or attacker-supplied URLs from a scraped posting — into the 4dayweek provider's URL path.
Common situations: Config pointing at a mirror or proxy of 4dayweek.io; URLs harvested from a page that link to the actual employer ATS rather than 4dayweek.io; a typo'd or lookalike domain; environment-specific overrides swapping in a staging host.
Related errors
- a16z-speedrun-talent: untrusted hostname "${parsed.hostname}
- weworkremotely: untrusted hostname "${parsed.hostname}" - mu
- workable: untrusted hostname "${parsed.hostname}" — must be
- refusing to archive restricted destination: ${preGuard.reaso
- Invalid or blocked URL: ${rejected.reason}
AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01).
Data as JSON: /api/errors/ca065e2976f629b6.
Report an issue: GitHub.