Repository: vercel/next.js
File: packages/next/src/server/app-render/csrf-protection.ts (TypeScript)

Code before:
// micromatch is only available at node runtime, so it cannot be used here since the code path that calls this function
// can be run from edge. This is a simple implementation that safely achieves the required functionality.
// the goal is to match the functionality for remotePatterns as defined here -
// https://nextjs.org/docs/app/api-reference/components/image#remotepatterns
// TODO - retrofit micromatch to work in edge and use that instead
function matchWildcardDomain(domain: string, pattern: string) {
  // Normalize: DNS is case-insensitive (RFC 1035) and trailing dots are optional
  const normalizedDomain = domain.toLowerCase().replace(/\.$/, '')
  const normalizedPattern = pattern.toLowerCase().replace(/\.$/, '')

  const domainParts = normalizedDomain.split('.')
  const patternParts = normalizedPattern.split('.')

  if (patternParts.length < 1) {
    // pattern is empty and therefore invalid to match against
    return false
  }

  if (domainParts.length < patternParts.length) {
    // domain has too few segments and thus cannot match
    return false
  }

  // Prevent wildcards from matching entire domains (e.g. '**' or '*.com')
  // This ensures wildcards can only match subdomains, not the main domain
  if (
    patternParts.length === 1 &&
    (patternParts[0] === '*' || patternParts[0] === '**')
  ) {
    return false
  }

  while (patternParts.length) {
    const patternPart = patternParts.pop()

Review comment (security):
How does this handle punycode domain names? Is it possible that this gets a unicode-encoded domain and tries to lowercase that, because that would be a security vulnerability as RFC 1035 only covers 7-bit ASCII. It's hard for me to know for certain if this function gets the punycode representation or the unicode representation.

You can address this in one of two ways:
- Write your own `toLowerCase` implementation that's careful to only modify A-Z.
- Add an e2e test to tests/e2e that proves this does not try to lower-case unicode characters. I'm not sure how you'd mock the DNS lookup though.

Code after:
// micromatch is only available at node runtime, so it cannot be used here since the code path that calls this function
// can be run from edge. This is a simple implementation that safely achieves the required functionality.
// the goal is to match the functionality for remotePatterns as defined here -
// https://nextjs.org/docs/app/api-reference/components/image#remotepatterns
// TODO - retrofit micromatch to work in edge and use that instead
function matchWildcardDomain(domain: string, pattern: string) {
  // DNS names are case-insensitive per RFC 1035
  // Use ASCII-only toLowerCase to avoid unicode issues
  const normalizedDomain = domain.replace(/[A-Z]/g, (c) => c.toLowerCase())
  const normalizedPattern = pattern.replace(/[A-Z]/g, (c) => c.toLowerCase())

  const domainParts = normalizedDomain.split('.')
  const patternParts = normalizedPattern.split('.')

  if (patternParts.length < 1) {
    // pattern is empty and therefore invalid to match against
    return false
  }

  if (domainParts.length < patternParts.length) {
    // domain has too few segments and thus cannot match
    return false
  }

  // Prevent wildcards from matching entire domains (e.g. '**' or '*.com')
  // This ensures wildcards can only match subdomains, not the main domain
  if (
    patternParts.length === 1 &&
    (patternParts[0] === '*' || patternParts[0] === '**')
  ) {
    return false
  }

  while (patternParts.length) {