hcengineering/platform · error · ApiError

Cannot process provided link

Error message

Cannot process provided link

What it means

The /print endpoint only renders links whose protocol is http: or https: and, when a hostname whitelist is configured for the server (allowedHostnames), whose hostname is in that whitelist. Any other link is rejected with this 403 ApiError. This is an SSRF / access-control guard, not a formatting problem.

Source

Thrown at services/print/pod-print/src/server.ts:214

  app.use(cors())
  app.use(express.json())
  app.use(withMeasureContext({ ctx: measureCtx }))

  app.get(
    '/print',
    wrapRequest(async (req, res, wsIds, wsLoginInfo) => {
      const ctx = req.ctx
      const rawlink = req.query.link as string
      const link = decodeURIComponent(rawlink)

      // Verify that link is from the same host and protocol is among the allowed
      const url = new URL(link)
      if (
        !['http:', 'https:'].includes(url.protocol) ||
        (whitelistedHostnames != null && !whitelistedHostnames.has(url.hostname))
      ) {
        ctx.error('Rejected processing unexpected link', { link })
        throw new ApiError(403, 'Cannot process provided link')
      }

      const options = parsePrintOptions(req.query)

      const printRes = await ctx.with(
        'print',
        { kind: options.kind, orientation: options.orientation },
        (ctx) => print(ctx, link, options),
        {
          url,
          viewport: options.viewport
        }
      )

      if (printRes === undefined) {
        throw new ApiError(400, 'Failed to print')
      }

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Use an https:// (or http://) URL for the link parameter.
  2. Ask the operator to add your link's hostname to the pod's allowedHostnames configuration, or run the pod with an empty whitelist (whitelist disabled) if appropriate.
  3. Verify exact hostname match — the check compares url.hostname against the Set exactly (no wildcards, no case folding).
  4. Make sure the link is properly encoded once, not double-encoded, so the decoded hostname is the intended one.

Example fix

// before
fetch(`/print?link=${encodeURIComponent('file:///tmp/doc.html')}`)
// after
const target = 'https://docs.example.com/report'
fetch(`/print?link=${encodeURIComponent(target)}`) // https:// and whitelisted hostname
Defensive patterns

Strategy: validation

Validate before calling

function assertPrintableUrl (raw: string, allowedHostnames?: string[]): URL {
  const url = new URL(decodeURIComponent(raw))
  if (!['http:', 'https:'].includes(url.protocol)) {
    throw new Error(`protocol not allowed: ${url.protocol}`)
  }
  if (allowedHostnames != null && allowedHostnames.length > 0 && !allowedHostnames.includes(url.hostname)) {
    throw new Error(`hostname not whitelisted: ${url.hostname}`)
  }
  return url
}

Type guard

function isHttpUrl (v: unknown): v is URL {
  try { const u = new URL(String(v)); return u.protocol === 'http:' || u.protocol === 'https:' } catch { return false }
}

Try / catch

try {
  const res = await fetch(`/print?link=${encodeURIComponent(link)}`)
  if (res.status === 403) {
    const body = await res.json()
    throw new Error(`Link rejected by print service (403): ${body.message} — check protocol and hostname whitelist`)
  }
  return await res.json()
} catch (err) { /* handle */ }

Prevention

When it happens

Trigger: GET /print?link=file:///etc/passwd or link=javascript:... (bad protocol); or an http(s) link whose hostname is not in the server's allowedHostnames list; also a hostname that differs in case/subdomain from the whitelisted entry (Set lookup is exact).

Common situations: Deploying the pod with allowedHostnames configured but the app generating links with a different domain (e.g. internal k8s service name vs public domain); printing localhost links that were never whitelisted; client sending encoded URIs whose decoded hostname doesn't match.

Related errors


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