{"record":{"id":"90e7761867c2327e","repo":"santifer/career-ops","slug":"senjob-url-must-use-https-url","errorCode":null,"errorMessage":"senjob: URL must use HTTPS: ${url}","messagePattern":"senjob: URL must use HTTPS: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/senjob.mjs","lineNumber":79,"sourceCode":"\n/** The machine-readable publication date, hidden next to its localized form. */\nconst HIDDEN_ISO_DATE_RE = /display:\\s*none;?\\s*\"?>\\s*(\\d{4}-\\d{2}-\\d{2})\\s*</i;\n\n/** @param {any} ctx @param {number} ms */\nfunction sleep(ctx, ms) {\n  if (typeof ctx?.sleep === 'function') return ctx.sleep(ms);\n  return new Promise((r) => setTimeout(r, ms));\n}\n\n/** @param {string} url */\nfunction assertSenjobUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`senjob: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`senjob: URL must use HTTPS: ${url}`);\n  if (parsed.hostname !== TRUSTED_HOST) {\n    throw new Error(`senjob: untrusted hostname \"${parsed.hostname}\" — must be ${TRUSTED_HOST}`);\n  }\n  return url;\n}\n\n/**\n * Collapse a markup fragment to its visible text.\n * Comments are stripped FIRST: the anchor bodies carry `<!-- d ico postulez -->`\n * between the title and a spacer image, and a naive tag strip would leave the\n * comment body sitting inside the title.\n * @param {string} fragment\n * @returns {string}\n */\nexport function visibleText(fragment) {\n  return decodeEntities(\n    String(fragment ?? '')\n      .replace(/<!--[\\s\\S]*?-->/g, ' ')","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/santifer/career-ops/blob/1696bec4d021768e7359f9aad6b329cba883da20/providers/senjob.mjs#L61-L97","documentation":"The senjob provider enforces https: on every URL it fetches. assertSenjobUrl parses the URL and throws this error when parsed.protocol is anything other than 'https:'. It complements the hostname check and the redirect:'error' fetch option to pin all requests to a secure senjob.com.","triggerScenarios":"assertSenjobUrl called with an http://, ftp://, or otherwise non-https URL — typically a hand-built list URL like http://senjob.com/offres-d-emploi.php or a config value stored without the scheme upgrade.","commonSituations":"Hardcoding http:// in a local/dev override; a config migration that downgraded URLs; concatenating 'http://' + host when assembling URLs in custom code.","solutions":["Use https:// URLs — ideally build them with buildListUrl(page) rather than by hand","Update any stored config/env value from http://senjob.com to https://senjob.com","If you need plain http for debugging, intercept at the proxy layer; do not downgrade the URL scheme"],"exampleFix":"// before\nconst url = 'http://senjob.com/offres-d-emploi.php?page=2';\n// after\nconst url = buildListUrl(2); // https://senjob.com/offres-d-emploi.php?page=2","handlingStrategy":"validation","validationCode":"function isHttpsSenjobUrl(url) {\n  try { return new URL(url).protocol === 'https:'; } catch { return false; }\n}\nif (!isHttpsSenjobUrl(url)) url = url.replace(/^http:/i, 'https:');","typeGuard":"function isHttps(url) {\n  try { return new URL(url).protocol === 'https:'; } catch { return false; }\n}","tryCatchPattern":"try {\n  assertSenjobUrl(url);\n} catch (err) {\n  if (String(err.message).startsWith('senjob: URL must use HTTPS')) {\n    console.error(`Non-https URL refused: ${err.message} — upgrade the scheme or use buildListUrl()`);\n    return null;\n  }\n  throw err;\n}","preventionTips":["Store all board URLs with the https:// scheme in config — never http://","Use buildListUrl(page) so the scheme comes from the shared constant","Treat the https requirement as a security invariant; upgrade inputs rather than weakening the guard","Lint portal configuration for http:// occurrences on scrape targets"],"tags":["url-validation","https","security"],"backgroundTag":"insecure-url-scheme","analyzedSha":"1696bec4d021768e7359f9aad6b329cba883da20","analyzedAt":"2026-09-01T19:19:23.111Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}