{"record":{"id":"3a8397537089cf30","repo":"santifer/career-ops","slug":"solidjobs-url-must-use-https-url","errorCode":null,"errorMessage":"solidjobs: URL must use HTTPS: ${url}","messagePattern":"solidjobs: URL must use HTTPS: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/solidjobs.mjs","lineNumber":27,"sourceCode":"\nconst ALLOWED_HOSTS = new Set(['solid.jobs']);\n\n/**\n * Validates that the provided URL is a trusted SolidJobs API endpoint.\n * Enforces HTTPS protocol, strict hostname matching, and required path prefix.\n * \n * @param {string} url - The URL string to validate.\n * @returns {string} The validated URL string.\n * @throws {Error} If the URL is malformed, uses non-HTTPS, has an untrusted host, or wrong path.\n */\nfunction assertUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`solidjobs: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`solidjobs: URL must use HTTPS: ${url}`);\n  if (!ALLOWED_HOSTS.has(parsed.hostname))\n    throw new Error(`solidjobs: untrusted hostname \"${parsed.hostname}\" — must be solid.jobs`);\n  if (!parsed.pathname.startsWith('/public-api/offers/'))\n    throw new Error(`solidjobs: URL path must start with /public-api/offers/: ${url}`);\n  return url;\n}\n\n/** @type {Provider} */\nexport default {\n  id: 'solidjobs',\n\n  /**\n   * Attempts to detect if the provider can handle the given entry by checking the careers_url.\n   * * @param {{ careers_url?: string, name?: string }} entry - The configuration entry.\n   * @returns {{url: string} | null} An object with the matched URL, or null if not matched.\n   */\n  detect(entry) {\n    const url = entry.careers_url || '';","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/solidjobs.mjs#L9-L45","documentation":"assertUrl enforces HTTPS for every SolidJobs API URL: a parseable URL whose protocol is not https: (usually http:) is rejected. The public API at solid.jobs is only consumed over TLS, both for security and because redirect:'error' + HTTPS is part of the provider's SSRF posture.","triggerScenarios":"Configuring careers_url as http://solid.jobs/public-api/offers/it; calling assertUrl directly in tests with an http:// URL; a proxy or local rewrite that downgrades the scheme.","commonSituations":"Typing http:// out of habit in portals.yml; an old config from before an HTTPS migration; string interpolation that lost the 's'; mirrored/internal endpoints using plain HTTP.","solutions":["Change the scheme to https:// in careers_url","Grep portals.yml for http://solid.jobs and fix all occurrences","Rely on detect()/fetch building URLs from the documented https form rather than pasting raw http URLs","If redirect downgrades occur, keep redirect:'error' and do not follow to http targets"],"exampleFix":"// before\ncareers_url: 'http://solid.jobs/public-api/offers/it'\n// after\ncareers_url: 'https://solid.jobs/public-api/offers/it'","handlingStrategy":"validation","validationCode":"function isHttpsSolidjobsUrl(url) {\n  try {\n    const u = new URL(url);\n    return u.protocol === 'https:' && u.hostname === 'solid.jobs';\n  } catch { return false; }\n}\nif (!isHttpsSolidjobsUrl(entry.careers_url)) throw new Error('solidjobs requires https://solid.jobs URLs');","typeGuard":null,"tryCatchPattern":"try {\n  await solidjobsProvider.fetch(entry, ctx);\n} catch (e) {\n  if (String(e.message).includes('must use HTTPS')) {\n    console.error(`Entry ${entry.name}: upgrade ${entry.careers_url} to https://`);\n  } else throw e;\n}","preventionTips":["Standardize on https:// in all portal config; ban http:// in validation","Remember the provider uses redirect:'error' so an http URL cannot silently succeed after a redirect","Grep config for http://solid.jobs on every config change","Prefer letting detect() accept entries rather than hand-pasting raw URLs"],"tags":["url-validation","https","security","config-error"],"backgroundTag":"invalid-url","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"}