santifer/career-ops · error · Error
pinpoint: untrusted hostname "${parsed.hostname}" — must mat
Error message
pinpoint: untrusted hostname "${parsed.hostname}" — must match <slug>.pinpointhq.com What it means
The pinpoint provider rejects an API URL whose hostname does not match the strict allowlist regex PINPOINT_HOST_RE (/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.pinpointhq\.com$/). This is an SSRF guard: it pins every request to a genuine <slug>.pinpointhq.com tenant subdomain so a crafted entry cannot redirect the fetch to an attacker-controlled or internal host. The slug must start and end alphanumeric with optional interior hyphens.
Source
Thrown at providers/pinpoint.mjs:34
// The tenant label must be a valid DNS label: it may contain hyphens but must
// not start or end with one (so `acme-.pinpointhq.com` is rejected). The
// optional trailing group keeps single-character labels (e.g. `a.pinpointhq.com`)
// valid. detect() and fetch() both route through this constant via
// resolveApiUrl()/assertPinpointUrl(), so the stricter check applies everywhere.
const PINPOINT_HOST_RE = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.pinpointhq\.com$/;
/** @param {string} url */
function assertPinpointUrl(url) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`pinpoint: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`pinpoint: URL must use HTTPS: ${url}`);
if (!PINPOINT_HOST_RE.test(parsed.hostname)) {
throw new Error(`pinpoint: untrusted hostname "${parsed.hostname}" — must match <slug>.pinpointhq.com`);
}
return url;
}
function resolveApiUrl(entry) {
const raw = 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;
if (!PINPOINT_HOST_RE.test(parsed.hostname)) return null;
return `https://${parsed.hostname}/postings.json`;
}
View on GitHub (pinned to 9b17a8ac97)
Solutions
- Confirm the entry's careers_url is literally https://<slug>.pinpointhq.com where <slug> is lowercase, starts and ends with a letter or digit, and uses only hyphens in between.
- If the tenant uses a branded custom domain, the pinpoint provider cannot auto-derive it — set provider explicitly to the correct provider or supply api: with the canonical pinpointhq.com subdomain.
- Strip any trailing slash, port, or uppercase characters from the hostname before validation.
- Verify the entry object passed to fetch() has not been mutated downstream to carry a resolved URL from a different host.
Example fix
// before
const entry = { name: 'Acme', careers_url: 'https://jobs.acme.com' };
// after — canonical Pinpoint subdomain
const entry = { name: 'Acme', careers_url: 'https://acme.pinpointhq.com' }; Defensive patterns
Strategy: validation
Validate before calling
const PINPOINT_HOST_RE = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.pinpointhq\.com$/;
function isValidPinpointUrl(url) {
try {
const p = new URL(url);
return p.protocol === 'https:' && PINPOINT_HOST_RE.test(p.hostname);
} catch { return false; }
}
// call before provider.fetch
if (!isValidPinpointUrl(entry.careers_url)) {
console.warn(`skip ${entry.name}: not a valid pinpointhq.com URL`);
} Type guard
null
Try / catch
try {
await provider.fetch(entry, ctx);
} catch (e) {
if (/pinpoint: untrusted hostname/.test(e.message)) {
// config issue, not transient — log and skip this entry
console.warn(`[skip] ${entry.name}: ${e.message}`);
} else throw e;
} Prevention
- Always run provider.detect(entry) before fetch(); it returns null instead of throwing when the URL does not resolve.
- Normalize careers_url to lowercase and strip trailing slashes before storing.
- Maintain a config validator that checks every pinpoint entry against the hostname regex at load time.
When it happens
Trigger: assertPinpointUrl throws when parsed.hostname fails the regex — e.g. a careers_url pointing to a custom branded domain (jobs.acme.com), a hostname with uppercase letters (Acme.pinpointhq.com), a leading/trailing hyphen in the slug (-acme.pinpointhq.com), or a hostname like pinpointhq.com with no slug prefix at all.
Common situations: A job_boards entry was auto-detected from a non-Pinpoint URL but routed to the pinpoint provider; a user pasted a Pinpoint vanity/branded domain that does not carry the pinpointhq.com suffix; the entry's careers_url was typo'd with a trailing dot or wrong TLD.
Related errors
- recruitee: untrusted hostname "${parsed.hostname}" — must ma
- remotli: untrusted hostname "${parsed.hostname}" — must be r
- rippling: untrusted hostname "${parsed.hostname}" — must be
- Refusing non-HTTP(S) URL: ${url}
- larajobs: untrusted hostname "${parsed.hostname}" - must be
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/280485972fd101e4.
Report an issue: GitHub.