santifer/career-ops · error · Error

H1B_API_BASE must not embed credentials.

Error message

H1B_API_BASE must not embed credentials.

What it means

resolveBase() rejects an H1B_API_BASE that embeds userinfo credentials (user:password@ in the URL). Undici refuses credentialed Requests anyway, and the URL is echoed to stdout via the source field, so credentials would be printed. The library throws a named error instead of leaking a secret.

Source

Thrown at plugins/h1b-sponsor/lib/api.mjs:55

  // (an unset shell variable, an empty .env line, a CI secret that did not
  // populate), and silently falling back would send someone's shortlist and
  // their token to a host they believed they had replaced.
  if (raw === undefined) return DEFAULT_BASE;
  const trimmed = String(raw).trim();
  if (!trimmed) {
    throw new Error('H1B_API_BASE is set but empty. Unset it to use the default endpoint.');
  }

  let parsed;
  try {
    parsed = new URL(trimmed);
  } catch {
    throw new Error(`H1B_API_BASE is not a valid URL: ${trimmed}`);
  }
  if (parsed.username || parsed.password) {
    // Undici refuses a credentialed Request anyway, and the value reaches
    // stdout through the source field, so this would print a password.
    throw new Error('H1B_API_BASE must not embed credentials.');
  }
  if (parsed.search || parsed.hash) {
    // Paths are appended, so a query or fragment swallows them: the request
    // would go to the base itself and answer about a company never asked for.
    throw new Error('H1B_API_BASE must not contain a query string or a fragment.');
  }
  // Plain http would put an Authorization header on the wire in the clear.
  // Loopback is exempt so a self-hoster can develop against a local worker,
  // but only for http. Exempting every scheme on a loopback host let
  // ftp://localhost and ws://localhost past validation, and those die later
  // inside fetch as a bare "fetch failed", which is the opaque failure this
  // check exists to replace with a named configuration error.
  const loopback = /^(localhost|127\.\d+\.\d+\.\d+|\[::1\]|::1)$/i.test(parsed.hostname);
  const allowedScheme = parsed.protocol === 'https:' || (parsed.protocol === 'http:' && loopback);
  if (!allowedScheme) {
    throw new Error(`H1B_API_BASE must use https, or http on loopback: ${trimmed}`);
  }
  // Appended as `${base}/employers/...`, so a trailing slash would double up.

View on GitHub (pinned to 1696bec4d0)

Solutions

  1. Remove the user:password@ portion from H1B_API_BASE and keep only scheme+host+path.
  2. Pass credentials out-of-band if the backend requires them — e.g. an Authorization header or a token env var consumed by your proxy — not in the URL.
  3. If the API is behind basic auth, front it with a local reverse proxy that injects the header, and point H1B_API_BASE at the proxy.

Example fix

// before (.env)
H1B_API_BASE=https://admin:s3cret@h1b.example.com
// after
H1B_API_BASE=https://h1b.example.com
Defensive patterns

Strategy: validation

Validate before calling

function hasEmbeddedCredentials(v) {
  try { const u = new URL(v); return Boolean(u.username || u.password); }
  catch { return false; }
}
// assert before running: if (hasEmbeddedCredentials(process.env.H1B_API_BASE)) throw ...

Type guard

function isCredentialFreeUrl(v) {
  try { const u = new URL(v); return !u.username && !u.password; } catch { return false; }
}

Try / catch

try {
  await lookupEmployer(name);
} catch (e) {
  if (e.message === 'H1B_API_BASE must not embed credentials.') {
    console.error('Remove user:pass@ from H1B_API_BASE; pass secrets via headers/proxy instead.');
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Setting H1B_API_BASE to something like https://user:token@h1b.example.com and calling any plugin API function that builds a request from apiBase().

Common situations: Putting an API token into the URL as basic-auth credentials because a cURL example used -u or user:pass@host syntax; copying an authenticated URL from a browser or proxy tool.

Related errors


AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01). Data as JSON: /api/errors/b042478848b2da55. Report an issue: GitHub.