hcengineering/platform · error · LinkPreviewError

INVALID_PROTOCOL

INVALID_PROTOCOL

Error message

Invalid protocol: ${url.protocol}. Only HTTP and HTTPS are allowed.

What it means

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.

Solutions

  1. Restrict input to http(s) URLs before calling the API
  2. Convert supported schemes (e.g. ftp links stored as web mirrors) to https equivalents
  3. Reject or strip non-http(s) links at UI/ingest time

Example fix

// before
validateUrl('ftp://files.example.com/doc') // throws
// after
validateUrl('https://files.example.com/doc')
Defensive patterns

Strategy: validation

Validate before calling

function isHttpProtocol(s: string): boolean {
  try { const u = new URL(s.trim()); return ['http:', 'https:'].includes(u.protocol) } catch { return false }
}
if (!isHttpProtocol(input)) throw new Error('only http/https URLs are supported')

Try / catch

try {
  return await fetchOEmbedData(client, url)
} catch (err) {
  if (err instanceof LinkPreviewError && err.code === 'INVALID_PROTOCOL') {
    console.warn('unsupported protocol, skipping preview for', url); return null
  }
  throw err
}

Prevention

When it happens

Trigger: Passing URLs with schemes like 'file:///etc/passwd', 'ftp://...', 'javascript:alert(1)', or 'data:text/html,...' to any function that calls validateUrl.

Common situations: Stored links with ftp/file schemes from legacy data; malicious input probing for XSS/SSRF; mailto: or tel: links pasted into preview fields.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/c444ff4689d5c3c3. Report an issue: GitHub.

Appendix: source

Thrown at pods/link-preview/src/parse.ts:216

  // Hostname is not an IP literal. Keep legacy explicit localhost-ish blocks.
  // (We intentionally do not attempt DNS resolution here.)
  if (host.endsWith('.localhost')) return true

  return false
}

function validateUrl (urlString: string): URL {
  let url: URL
  try {
    url = new URL(urlString)
  } catch {
    throw new LinkPreviewError(`Invalid URL: ${urlString}`, 'INVALID_URL')
  }

  // Only allow HTTP(S) protocols
  if (!['http:', 'https:'].includes(url.protocol)) {
    throw new LinkPreviewError(
      `Invalid protocol: ${url.protocol}. Only HTTP and HTTPS are allowed.`,
      'INVALID_PROTOCOL'
    )
  }

  // SSRF protection: block private/internal hosts and IP literals (incl. IPv6-mapped IPv4)
  if (isBlockedHost(url.hostname)) {
    throw new LinkPreviewError('Blocked URL: Access to internal addresses is not allowed.', 'BLOCKED_URL')
  }

  return url
}

function isRedirectStatus (status: number): boolean {
  return status >= 300 && status < 400
}

async function fetchWithValidatedRedirects (

View on GitHub (pinned to 63e28dc964)