santifer/career-ops · error · Error
rippling: untrusted hostname
Error message
rippling: untrusted hostname "${parsed.hostname}" — must be ${API_HOST} What it means
The rippling provider pins requests to a single trusted API host (API_HOST). assertRipplingApiUrl compares the parsed hostname against API_HOST and throws this error on mismatch, preventing SSRF and accidental calls to lookalike or mirror domains.
Solutions
- Use the exact API host expected (compare your URL's hostname to the API_HOST constant in providers/rippling.mjs).
- Fix typos or subdomain mistakes in the entry config.
- Route through proxies at the network layer, not by rewriting the URL hostname.
Example fix
// before
url = 'https://rippling.com/api/ats/jobs';
// after
url = `https://${API_HOST}/api/ats/jobs`; // API_HOST e.g. api.rippling.com Defensive patterns
Strategy: validation
Validate before calling
const expectedHost = 'api.rippling.com'; // mirror of API_HOST
const hostOk = (u) => { try { return new URL(u).hostname === expectedHost; } catch { return false; } };
if (!hostOk(entry.url)) throw new Error(`skip: ${entry.url} is not the Rippling API host`); Type guard
const isRipplingApi = (u) => { try { return new URL(u).hostname === 'api.rippling.com'; } catch { return false; } }; Try / catch
try { await provider.fetch(entry, ctx); } catch (e) { if (e.message.includes('untrusted hostname')) { console.error(`Entry ${entry.name} URL host mismatch`); return null; } throw e; } Prevention
- Build API URLs from the API_HOST constant, never from raw config strings.
- Allowlist hostnames before calling any provider.
- Don't route proxies by rewriting the URL hostname.
When it happens
Trigger: A rippling API URL whose parsed hostname !== API_HOST — e.g. https://rippling.com/... instead of the API subdomain, a typo'd host, or an attacker/config-supplied foreign hostname.
Common situations: Using the marketing domain instead of the API host, DNS/search-replaced URLs, proxies embedded in the URL, or config values copied from another provider.
Understand the failure class
Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.
Related errors
- remotli: untrusted hostname
- Access denied: Localhost or internal domain target detected.
- agentic-jobs: untrusted hostname
- eightfold: untrusted hostname
- getonbrd: untrusted hostname
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/668c6254e64d14c7.
Report an issue: GitHub.
Appendix: source
Thrown at providers/rippling.mjs:58
return segment;
}
/** Build the board API URL for a validated slug. */
function apiUrlForSlug(slug) {
return `${API_BASE}/${encodeURIComponent(slug)}/jobs`;
}
/** @param {string} url */
function assertRipplingApiUrl(url) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`rippling: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`rippling: URL must use HTTPS: ${url}`);
if (parsed.hostname !== API_HOST) {
throw new Error(`rippling: untrusted hostname "${parsed.hostname}" — must be ${API_HOST}`);
}
return url;
}
/** @type {Provider} */
export default {
id: 'rippling',
detect(entry) {
const slug = resolveSlug(entry);
return slug ? { url: apiUrlForSlug(slug) } : null;
},
async fetch(entry, ctx) {
const slug = resolveSlug(entry);
if (!slug) throw new Error(`rippling: cannot derive API URL for ${entry.name}`);
const apiUrl = apiUrlForSlug(slug);
assertRipplingApiUrl(apiUrl);View on GitHub (pinned to aac998c7ed)