{"record":{"id":"8bd4bb1541595fa0","repo":"santifer/career-ops","slug":"nodesk-url-must-use-https-url","errorCode":null,"errorMessage":"nodesk: URL must use HTTPS: ${url}","messagePattern":"nodesk: URL must use HTTPS: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/nodesk.mjs","lineNumber":22,"sourceCode":"// NoDesk provider - board-wide RSS feed\n// (https://nodesk.co/remote-jobs/index.xml). The feed is public, no-auth,\n// and XML, so it is parsed in-process with the same tiny tag extractor\n// approach as providers/personio.mjs rather than adding an XML dependency.\n//\n// Wire in via a `job_boards:` entry with `provider: nodesk`.\n\nconst FEED_URL = 'https://nodesk.co/remote-jobs/index.xml';\nconst TRUSTED_HOST = 'nodesk.co';\n\n/** @param {string} url */\nfunction assertNodeskUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`nodesk: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`nodesk: URL must use HTTPS: ${url}`);\n  if (parsed.hostname !== TRUSTED_HOST) {\n    throw new Error(`nodesk: untrusted hostname \"${parsed.hostname}\" - must be ${TRUSTED_HOST}`);\n  }\n  return url;\n}\n\n// NaN-safe Date.parse - `|| undefined` would also coerce a valid epoch 0.\nfunction toEpochMs(value) {\n  if (!value) return undefined;\n  const parsed = Date.parse(value);\n  return Number.isNaN(parsed) ? undefined : parsed;\n}\n\nfunction fallbackCompany(entry) {\n  return typeof entry?.name === 'string' && entry.name.trim() ? entry.name.trim() : 'NoDesk';\n}\n\n/** @type {Provider} */","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/providers/nodesk.mjs#L4-L40","documentation":"Thrown by nodesk's assertNodeskUrl() after the URL parses successfully but its protocol is not 'https:'. This is the second SSRF gate: it prevents the provider from fetching over plaintext http://, which would expose the request to MITM and allow a redirect to an attacker-controlled host. The check runs before any network call.","triggerScenarios":"The URL string is parseable (passed new URL()) but uses http:, ftp:, file:, or another non-https scheme. The most direct trigger is a config entry with api: 'http://nodesk.co/...' instead of 'https://'. Also fires if someone tests with a localhost http URL or a file:// URL pointing at a fixture.","commonSituations":"A portals.yml entry was authored with http:// (common copy-paste from a browser that didn't upgrade). In test environments, a developer points the URL at http://localhost:port for a mock server. A config-generation tool that strips SSL or normalizes to http also produces this.","solutions":["Change the URL to use https:// — for portals.yml, update the api or careers_url field from http:// to https://.","If testing against a local mock server, either run the mock on HTTPS (e.g. with mkcert) or use a test-specific override that bypasses assertNodeskUrl only in test mode.","Verify no middleware or proxy is downgrading the stored URL scheme before it reaches the provider."],"exampleFix":"// before (portals.yml)\njob_boards:\n  nodesk:\n    provider: nodesk\n    api: 'http://nodesk.co/remote-jobs/index.xml'\n\n// after\njob_boards:\n  nodesk:\n    provider: nodesk\n    api: 'https://nodesk.co/remote-jobs/index.xml'","handlingStrategy":"validation","validationCode":"/** Ensure a URL uses HTTPS before passing to the provider. */\nfunction ensureHttps(url) {\n  if (typeof url !== 'string') return null;\n  return url.replace(/^http:\\/\\//i, 'https://');\n}\n\n// normalize config before scanning:\nentry.api = ensureHttps(entry.api) || entry.api;","typeGuard":"/** @param {string} url @returns {boolean} */\nfunction isHttpsUrl(url) {\n  try { return new URL(url).protocol === 'https:'; } catch { return false; }\n}","tryCatchPattern":"try {\n  await nodeskProvider.fetch(entry, ctx);\n} catch (err) {\n  if (String(err.message).includes('must use HTTPS')) {\n    entry.api = entry.api.replace(/^http:/i, 'https:');\n    // retry once with corrected scheme\n    await nodeskProvider.fetch(entry, ctx);\n  } else throw err;\n}","preventionTips":["Always author portal URLs with https:// in YAML config.","Run a config-lint script that flags http:// URLs at load time.","In test environments, use HTTPS mock servers (mkcert, self-signed certs) rather than http://localhost."],"tags":["url-validation","ssrf-guard","https","nodesk","config"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}