{"record":{"id":"c704aee728af0a80","repo":"santifer/career-ops","slug":"4dayweek-url-must-use-https-url","errorCode":null,"errorMessage":"4dayweek: URL must use HTTPS: ${url}","messagePattern":"4dayweek: URL must use HTTPS: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/4dayweek.mjs","lineNumber":55,"sourceCode":"      if (parsed.protocol === 'https:' && parsed.hostname === TRUSTED_HOST) {\n        return { url: FEED_BASE };\n      }\n    } catch {\n      // Ignore malformed URLs; another provider may still claim the entry.\n    }\n  }\n  return null;\n}\n\n/** @param {string} url */\nfunction assertFourDayUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`4dayweek: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`4dayweek: URL must use HTTPS: ${url}`);\n  if (parsed.hostname !== TRUSTED_HOST) {\n    throw new Error(`4dayweek: untrusted hostname \"${parsed.hostname}\" — must be ${TRUSTED_HOST}`);\n  }\n  return url;\n}\n\n/** Resolve the page cap: a positive integer `max_pages` on the entry, capped. */\nfunction resolveMaxPages(entry) {\n  const v = entry?.max_pages;\n  if (Number.isInteger(v) && v > 0) return Math.min(v, MAX_PAGES_CAP);\n  return DEFAULT_MAX_PAGES;\n}\n\n// NaN-safe: posted is epoch SECONDS → ms; anything non-finite yields undefined.\nfunction toEpochMs(seconds) {\n  return Number.isFinite(seconds) ? seconds * 1000 : undefined;\n}\n","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/santifer/career-ops/blob/1696bec4d021768e7359f9aad6b329cba883da20/providers/4dayweek.mjs#L37-L73","documentation":"Same allowlist guard as error 51, one check later: the URL parses, but its protocol is not 'https:'. The 4dayweek provider only accepts HTTPS to prevent sending requests or leaking context over plaintext HTTP.","triggerScenarios":"Passing a parseable URL whose scheme is http:, ftp:, file:, or anything non-https to assertFourDayUrl — e.g. 'http://4dayweek.io/job/1' or a file:// URL from a local config.","commonSituations":"Older config entries written before the provider enforced HTTPS; manually typed http:// URLs; URLs built by string concatenation that defaults to http; redirects not involved here — this fires before any request is made.","solutions":["Change the URL scheme to https:// in the config/entry it came from.","If the source is a site that serves http only, do not downgrade the check — the target must be reachable over HTTPS.","Search your portals.yml/feed entries for 'http://' and fix them to 'https://'."],"exampleFix":"// before\nassertFourDayUrl('http://4dayweek.io/job/9');\n// after\nassertFourDayUrl('https://4dayweek.io/job/9');","handlingStrategy":"validation","validationCode":"function isHttpsUrl(url) {\n  try { return new URL(url).protocol === 'https:'; } catch { return false; }\n}\nif (!isHttpsUrl(entry.url)) entry.url = entry.url.replace(/^http:/, 'https:');","typeGuard":"function isHttpsUrl(v) {\n  if (typeof v !== 'string') return false;\n  try { return new URL(v).protocol === 'https:'; } catch { return false; }\n}","tryCatchPattern":"try {\n  provider.check(url);\n} catch (err) {\n  if (err.message.startsWith('4dayweek: URL must use HTTPS')) {\n    console.warn(`Upgrading to HTTPS: ${url}`);\n    return provider.check(url.replace(/^http:/, 'https:'));\n  }\n  throw err;\n}","preventionTips":["Normalize http:// to https:// at config load, before entries reach the provider.","Never add http:// sources to provider entries; verify targets support TLS first.","Add a lint/test asserting every configured feed URL has protocol 'https:'."],"tags":["url-validation","https","ssrf-protection"],"backgroundTag":"insecure-http-url-rejected","analyzedSha":"1696bec4d021768e7359f9aad6b329cba883da20","analyzedAt":"2026-09-01T19:19:23.111Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}