{"version":3,"file":"tunnel.js","sources":["../../../src/utils/tunnel.ts"],"sourcesContent":["import { getEnvelopeEndpointWithUrlEncodedAuth } from '../api';\nimport { debug } from './debug-logger';\nimport { makeDsn } from './dsn';\nimport { parseEnvelope } from './envelope';\n\nexport interface HandleTunnelRequestOptions {\n  /** Incoming request containing the Sentry envelope as its body */\n  request: Request;\n  /** Pre-parsed array of allowed DSN strings */\n  allowedDsns: Array<string>;\n}\n\n/**\n * Core Sentry tunnel handler - framework agnostic.\n *\n * Validates the envelope DSN against allowed DSNs, then forwards the\n * envelope to the Sentry ingest endpoint.\n *\n * @returns A `Response` — either the upstream Sentry response on success, or an error response.\n */\nexport async function handleTunnelRequest(options: HandleTunnelRequestOptions): Promise<Response> {\n  const { request, allowedDsns } = options;\n\n  if (allowedDsns.length === 0) {\n    return new Response('Tunnel not configured', { status: 500 });\n  }\n\n  const body = new Uint8Array(await request.arrayBuffer());\n\n  let envelopeHeader;\n  try {\n    [envelopeHeader] = parseEnvelope(body);\n  } catch {\n    return new Response('Invalid envelope', { status: 400 });\n  }\n\n  if (!envelopeHeader) {\n    return new Response('Invalid envelope: missing header', { status: 400 });\n  }\n\n  const dsn = envelopeHeader.dsn;\n  if (!dsn) {\n    return new Response('Invalid envelope: missing DSN', { status: 400 });\n  }\n\n  // SECURITY: Validate that the envelope DSN matches one of the allowed DSNs\n  // This prevents SSRF attacks where attackers send crafted envelopes\n  // with malicious DSNs pointing to arbitrary hosts\n  const isAllowed = allowedDsns.some(allowed => allowed === dsn);\n\n  if (!isAllowed) {\n    debug.warn(`Sentry tunnel: rejected request with unauthorized DSN (${dsn})`);\n    return new Response('DSN not allowed', { status: 403 });\n  }\n\n  const dsnComponents = makeDsn(dsn);\n  if (!dsnComponents) {\n    debug.warn(`Could not extract DSN Components from: ${dsn}`);\n    return new Response('Invalid DSN', { status: 403 });\n  }\n\n  const sentryIngestUrl = getEnvelopeEndpointWithUrlEncodedAuth(dsnComponents);\n\n  try {\n    return await fetch(sentryIngestUrl, {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/x-sentry-envelope',\n      },\n      body,\n    });\n  } catch (error) {\n    debug.error('Sentry tunnel: failed to forward envelope', error);\n    return new Response('Failed to forward envelope to Sentry', { status: 500 });\n  }\n}\n"],"names":["parseEnvelope","dsn","debug","makeDsn","getEnvelopeEndpointWithUrlEncodedAuth"],"mappings":";;;;;;;AAYA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,eAAe,mBAAmB,CAAC,OAAO,EAAiD;AAClG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAA,EAAY,GAAI,OAAO;;AAE1C,EAAE,IAAI,WAAW,CAAC,MAAA,KAAW,CAAC,EAAE;AAChC,IAAI,OAAO,IAAI,QAAQ,CAAC,uBAAuB,EAAE,EAAE,MAAM,EAAE,GAAA,EAAK,CAAC;AACjE,EAAE;;AAEF,EAAE,MAAM,IAAA,GAAO,IAAI,UAAU,CAAC,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC;;AAE1D,EAAE,IAAI,cAAc;AACpB,EAAE,IAAI;AACN,IAAI,CAAC,cAAc,CAAA,GAAIA,sBAAa,CAAC,IAAI,CAAC;AAC1C,EAAE,EAAE,MAAM;AACV,IAAI,OAAO,IAAI,QAAQ,CAAC,kBAAkB,EAAE,EAAE,MAAM,EAAE,GAAA,EAAK,CAAC;AAC5D,EAAE;;AAEF,EAAE,IAAI,CAAC,cAAc,EAAE;AACvB,IAAI,OAAO,IAAI,QAAQ,CAAC,kCAAkC,EAAE,EAAE,MAAM,EAAE,GAAA,EAAK,CAAC;AAC5E,EAAE;;AAEF,EAAE,MAAMC,KAAA,GAAM,cAAc,CAAC,GAAG;AAChC,EAAE,IAAI,CAACA,KAAG,EAAE;AACZ,IAAI,OAAO,IAAI,QAAQ,CAAC,+BAA+B,EAAE,EAAE,MAAM,EAAE,GAAA,EAAK,CAAC;AACzE,EAAE;;AAEF;AACA;AACA;AACA,EAAE,MAAM,SAAA,GAAY,WAAW,CAAC,IAAI,CAAC,OAAA,IAAW,OAAA,KAAYA,KAAG,CAAC;;AAEhE,EAAE,IAAI,CAAC,SAAS,EAAE;AAClB,IAAIC,iBAAK,CAAC,IAAI,CAAC,CAAC,uDAAuD,EAAED,KAAG,CAAC,CAAC,CAAC,CAAC;AAChF,IAAI,OAAO,IAAI,QAAQ,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,GAAA,EAAK,CAAC;AAC3D,EAAE;;AAEF,EAAE,MAAM,aAAA,GAAgBE,WAAO,CAACF,KAAG,CAAC;AACpC,EAAE,IAAI,CAAC,aAAa,EAAE;AACtB,IAAIC,iBAAK,CAAC,IAAI,CAAC,CAAC,uCAAuC,EAAED,KAAG,CAAC,CAAA,CAAA;AACA,IAAA,OAAA,IAAA,QAAA,CAAA,aAAA,EAAA,EAAA,MAAA,EAAA,GAAA,EAAA,CAAA;AACA,EAAA;;AAEA,EAAA,MAAA,eAAA,GAAAG,yCAAA,CAAA,aAAA,CAAA;;AAEA,EAAA,IAAA;AACA,IAAA,OAAA,MAAA,KAAA,CAAA,eAAA,EAAA;AACA,MAAA,MAAA,EAAA,MAAA;AACA,MAAA,OAAA,EAAA;AACA,QAAA,cAAA,EAAA,+BAAA;AACA,OAAA;AACA,MAAA,IAAA;AACA,KAAA,CAAA;AACA,EAAA,CAAA,CAAA,OAAA,KAAA,EAAA;AACA,IAAAF,iBAAA,CAAA,KAAA,CAAA,2CAAA,EAAA,KAAA,CAAA;AACA,IAAA,OAAA,IAAA,QAAA,CAAA,sCAAA,EAAA,EAAA,MAAA,EAAA,GAAA,EAAA,CAAA;AACA,EAAA;AACA;;;;"}