{"record":{"id":"c444ff4689d5c3c3","repo":"hcengineering/platform","slug":"invalid-protocol","errorCode":"INVALID_PROTOCOL","errorMessage":"Invalid protocol: ${url.protocol}. Only HTTP and HTTPS are allowed.","messagePattern":"Invalid protocol: (.+?)\\. Only HTTP and HTTPS are allowed\\.","errorType":"error_code","errorClass":"LinkPreviewError","httpStatus":null,"severity":"error","filePath":"pods/link-preview/src/parse.ts","lineNumber":216,"sourceCode":"\n  // Hostname is not an IP literal. Keep legacy explicit localhost-ish blocks.\n  // (We intentionally do not attempt DNS resolution here.)\n  if (host.endsWith('.localhost')) return true\n\n  return false\n}\n\nfunction validateUrl (urlString: string): URL {\n  let url: URL\n  try {\n    url = new URL(urlString)\n  } catch {\n    throw new LinkPreviewError(`Invalid URL: ${urlString}`, 'INVALID_URL')\n  }\n\n  // Only allow HTTP(S) protocols\n  if (!['http:', 'https:'].includes(url.protocol)) {\n    throw new LinkPreviewError(\n      `Invalid protocol: ${url.protocol}. Only HTTP and HTTPS are allowed.`,\n      'INVALID_PROTOCOL'\n    )\n  }\n\n  // SSRF protection: block private/internal hosts and IP literals (incl. IPv6-mapped IPv4)\n  if (isBlockedHost(url.hostname)) {\n    throw new LinkPreviewError('Blocked URL: Access to internal addresses is not allowed.', 'BLOCKED_URL')\n  }\n\n  return url\n}\n\nfunction isRedirectStatus (status: number): boolean {\n  return status >= 300 && status < 400\n}\n\nasync function fetchWithValidatedRedirects (","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/pods/link-preview/src/parse.ts#L198-L234","documentation":"validateUrl only allows http: and https: protocols as a security measure. Any other scheme (ftp:, file:, javascript:, data:, etc.) raises LinkPreviewError with code INVALID_PROTOCOL.","triggerScenarios":"Passing URLs with schemes like 'file:///etc/passwd', 'ftp://...', 'javascript:alert(1)', or 'data:text/html,...' to any function that calls validateUrl.","commonSituations":"Stored links with ftp/file schemes from legacy data; malicious input probing for XSS/SSRF; mailto: or tel: links pasted into preview fields.","solutions":["Restrict input to http(s) URLs before calling the API","Convert supported schemes (e.g. ftp links stored as web mirrors) to https equivalents","Reject or strip non-http(s) links at UI/ingest time"],"exampleFix":"// before\nvalidateUrl('ftp://files.example.com/doc') // throws\n// after\nvalidateUrl('https://files.example.com/doc')","handlingStrategy":"validation","validationCode":"function isHttpProtocol(s: string): boolean {\n  try { const u = new URL(s.trim()); return ['http:', 'https:'].includes(u.protocol) } catch { return false }\n}\nif (!isHttpProtocol(input)) throw new Error('only http/https URLs are supported')","typeGuard":null,"tryCatchPattern":"try {\n  return await fetchOEmbedData(client, url)\n} catch (err) {\n  if (err instanceof LinkPreviewError && err.code === 'INVALID_PROTOCOL') {\n    console.warn('unsupported protocol, skipping preview for', url); return null\n  }\n  throw err\n}","preventionTips":["Sanitize stored links to http(s) only","Strip or convert ftp/file/mailto links at ingest time","Never pass raw user-supplied schemes without checking `url.protocol`"],"tags":["url","security","protocol","validation"],"backgroundTag":"invalid-url-protocol","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}