{"version":3,"sources":["../../../../../src/server/lib/router-utils/block-cross-site-dev.ts"],"sourcesContent":["import type { Duplex } from 'stream'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { parseUrl } from '../../../lib/url'\nimport { warnOnce } from '../../../build/output/log'\nimport { isCsrfOriginAllowed } from '../../app-render/csrf-protection'\n\nconst allowedDevOriginsDocs =\n  'https://nextjs.org/docs/app/api-reference/config/next-config-js/allowedDevOrigins'\n\nfunction getBlockedResourcePath(req: IncomingMessage): string {\n  return parseUrl(req.url ?? '')?.pathname ?? req.url ?? '/_next/*'\n}\n\nfunction formatBlockedCrossSiteMessage(\n  source: string | undefined,\n  resourcePath: string\n): string {\n  const lines = [\n    `Blocked cross-origin request to Next.js dev resource ${resourcePath}${getBlockedSourceDescription(source)}.`,\n    'Cross-origin access to Next.js dev resources is blocked by default for safety.',\n  ]\n\n  // `source` has 3 meanings here:\n  // - `'null'`: browser explicitly sent `Origin: null` for an opaque/sandboxed origin\n  // - hostname string: we parsed an allowlistable host from Origin/Referer\n  // - `undefined` (and effectively empty string): the request did not include a usable host\n  if (source === 'null') {\n    lines.push(\n      '',\n      'This request came from a privacy-sensitive or opaque origin, so Next.js cannot determine which host to allow.',\n      'If you need it to succeed, load the dev server from a normal origin and add that host to \"allowedDevOrigins\".'\n    )\n  } else if (source) {\n    lines.push(\n      '',\n      'To allow this host in development, add it to \"allowedDevOrigins\" in next.config.js and restart the dev server:',\n      '',\n      '// next.config.js',\n      'module.exports = {',\n      `  allowedDevOrigins: ['${source}'],`,\n      '}'\n    )\n  } else {\n    lines.push(\n      '',\n      'This request did not include an allowlistable source host.',\n      'If you need it to succeed, make sure the browser sends an Origin or Referer from a host listed in \"allowedDevOrigins\".'\n    )\n  }\n\n  lines.push('', `Read more: ${allowedDevOriginsDocs}`)\n  return lines.join('\\n')\n}\n\nfunction getBlockedSourceDescription(source: string | undefined): string {\n  if (source === 'null') {\n    return ' from a privacy-sensitive or opaque origin'\n  }\n\n  if (source) {\n    return ` from \"${source}\"`\n  }\n\n  return ' from an unknown source'\n}\n\nfunction blockRequest(\n  req: IncomingMessage,\n  res: ServerResponse | Duplex,\n  source: string | undefined\n): boolean {\n  warnOnce(formatBlockedCrossSiteMessage(source, getBlockedResourcePath(req)))\n\n  if ('statusCode' in res) {\n    res.statusCode = 403\n  }\n\n  res.end('Unauthorized')\n\n  return true\n}\n\nfunction parseHostnameFromHeader(\n  header: string | string[] | undefined\n): string | undefined {\n  const headerValue = Array.isArray(header) ? header[0] : header\n\n  if (!headerValue || headerValue === 'null') {\n    return\n  }\n\n  const parsedHeader = parseUrl(headerValue)\n  return parsedHeader?.hostname.toLowerCase()\n}\n\nfunction isInternalEndpoint(req: IncomingMessage): boolean {\n  if (!req.url) return false\n\n  try {\n    // TODO: We should standardize on a single prefix for this\n    const isMiddlewareRequest = req.url.includes('/__nextjs')\n    const isInternalAsset = req.url.includes('/_next')\n    // Static media requests are excluded, as they might be loaded via CSS and would fail\n    // CORS checks.\n    const isIgnoredRequest =\n      req.url.includes('/_next/image') ||\n      req.url.includes('/_next/static/media')\n\n    return !isIgnoredRequest && (isInternalAsset || isMiddlewareRequest)\n  } catch (err) {\n    return false\n  }\n}\n\nexport const blockCrossSiteDEV = (\n  req: IncomingMessage,\n  res: ServerResponse | Duplex,\n  allowedDevOrigins: string[] | undefined,\n  hostname: string | undefined\n): boolean => {\n  const allowedOrigins = [\n    '*.localhost',\n    'localhost',\n    ...(allowedDevOrigins ?? []),\n  ]\n  if (hostname) {\n    allowedOrigins.push(hostname)\n  }\n\n  // only process internal URLs/middleware\n  if (!isInternalEndpoint(req)) {\n    return false\n  }\n\n  // block non-cors request from cross-site e.g. script tag on\n  // different host\n  if (\n    req.headers['sec-fetch-mode'] === 'no-cors' &&\n    req.headers['sec-fetch-site'] === 'cross-site'\n  ) {\n    // no-cors requests do not send an Origin header, so fall back to Referer\n    // when validating configured cross-site script loads.\n    const refererHostname = parseHostnameFromHeader(req.headers['referer'])\n\n    if (\n      refererHostname &&\n      isCsrfOriginAllowed(refererHostname, allowedOrigins)\n    ) {\n      return false\n    }\n\n    return blockRequest(req, res, refererHostname)\n  }\n\n  // ensure websocket requests are only fulfilled from allowed origin\n  const rawOrigin = req.headers['origin']\n  const originHeader = Array.isArray(rawOrigin) ? rawOrigin[0] : rawOrigin\n  const parsedOrigin =\n    originHeader && originHeader !== 'null'\n      ? parseUrl(originHeader)\n      : originHeader\n\n  const originLowerCase =\n    parsedOrigin === undefined || typeof parsedOrigin === 'string'\n      ? parsedOrigin\n      : parsedOrigin.hostname.toLowerCase()\n\n  // Allow requests with no origin since those are just GET requests from same-site\n  return (\n    originLowerCase !== undefined &&\n    !isCsrfOriginAllowed(originLowerCase, allowedOrigins) &&\n    blockRequest(req, res, originLowerCase)\n  )\n}\n"],"names":["parseUrl","warnOnce","isCsrfOriginAllowed","allowedDevOriginsDocs","getBlockedResourcePath","req","url","pathname","formatBlockedCrossSiteMessage","source","resourcePath","lines","getBlockedSourceDescription","push","join","blockRequest","res","statusCode","end","parseHostnameFromHeader","header","headerValue","Array","isArray","parsedHeader","hostname","toLowerCase","isInternalEndpoint","isMiddlewareRequest","includes","isInternalAsset","isIgnoredRequest","err","blockCrossSiteDEV","allowedDevOrigins","allowedOrigins","headers","refererHostname","rawOrigin","originHeader","parsedOrigin","originLowerCase","undefined"],"mappings":"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","ignoreList":[0]}