{"record":{"id":"640c2df6fb58eabf","repo":"santifer/career-ops","slug":"themuse-url-must-use-https-url","errorCode":null,"errorMessage":"themuse: URL must use HTTPS: ${url}","messagePattern":"themuse: URL must use HTTPS: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/themuse.mjs","lineNumber":90,"sourceCode":"      // unbounded value would otherwise stall this board's fetch for as long\n      // as the server says, defeating the point of a bounded backoff.\n      const retryAfterMs = parseRetryAfterMs(err?.retryAfter);\n      const delayMs = retryAfterMs !== null ? Math.min(retryAfterMs, RETRY_MAX_DELAY_MS * 4) : (backoff + Math.random() * 250);\n      await sleep(delayMs, ctx);\n    }\n  }\n  throw lastErr;\n}\n\n/** @param {string} url */\nfunction assertMuseUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`themuse: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`themuse: URL must use HTTPS: ${url}`);\n  if (parsed.hostname !== TRUSTED_HOST) {\n    throw new Error(`themuse: untrusted hostname \"${parsed.hostname}\" — must be ${TRUSTED_HOST}`);\n  }\n  return url;\n}\n\n/**\n * Normalize a single result from the Muse API response. Exported for unit tests.\n *\n * Field mapping:\n *   name              → title\n *   refs.landing_page → url\n *   company.name      → company\n *   locations[0].name → location\n *\n * Returns null when required fields (title or url) are missing or invalid.\n *\n * @param {any} j","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/themuse.mjs#L72-L108","documentation":"assertMuseUrl() requires every URL to use the https: protocol. The URL parsed successfully but its scheme is something else (http:, ftp:, javascript:, etc.), so the provider refuses it. This is a deliberate security/consistency guard so the provider never fetches or emits insecure or exotic-scheme URLs.","triggerScenarios":"assertMuseUrl(url) is called with a parseable URL whose parsed.protocol !== 'https:' — typically an http:// link in the config or a schemeless-relative URL that resolved against a file: base.","commonSituations":"Copy-pasting a plain-http link from an old doc; a config entry written as 'http://www.themuse.com/...'; code building a URL from an env var defaulting to http.","solutions":["Change the scheme to https:// in the config or calling code.","Search your portals.yml for http:// occurrences and update them to https://.","If the upstream only serves http, do not downgrade the check — proxy or drop the entry; the Muse API itself is HTTPS-only."],"exampleFix":"// before\nassertMuseUrl('http://www.themuse.com/api/v2/jobs?page=0');\n// after\nassertMuseUrl('https://www.themuse.com/api/v2/jobs?page=0');","handlingStrategy":"validation","validationCode":"function isHttpsUrl(value) {\n  try { return new URL(value).protocol === 'https:'; } catch { return false; }\n}\n// pre-check: entries.filter(e => !isHttpsUrl(e.url)) → fix before running the scan","typeGuard":"const isHttps = (v) => {\n  if (typeof v !== 'string') return false;\n  try { return new URL(v).protocol === 'https:'; } catch { return false; }\n};","tryCatchPattern":"try {\n  return await provider.fetch(entry, ctx);\n} catch (err) {\n  if (String(err.message).includes('must use HTTPS')) {\n    const fixed = entry.url.replace(/^http:/, 'https:');\n    console.warn(`Upgraded ${entry.name} to ${fixed}; update portals.yml`);\n    return null;\n  }\n  throw err;\n}","preventionTips":["Grep config files for 'http://' and migrate them to 'https://' during setup","Default any URL template or env-var fallback to https, never http","Add a lint rule or pre-commit script enforcing https schemes on all portal URLs","Treat an http Muse link as a bug: the real API is HTTPS-only, so the entry is wrong anyway"],"tags":["url","https","security","themuse"],"backgroundTag":"invalid-url-format","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}