Repository: remix-run/react-router
File: packages/react-router/lib/actions.ts (TypeScript)

Code before:
export function throwIfPotentialCSRFAttack(
  headers: Headers,
  allowedActionOrigins: string[] | undefined,
) {
  let originHeader = headers.get("origin");
  let originDomain: string | null = null;

  try {
    originDomain =
      typeof originHeader === "string" && originHeader !== "null"
        ? new URL(originHeader).host
        : originHeader;
  } catch {
    // error parsing the Origin header as a valid URL
    throw new Error(
      `provided \`origin\` header is not a valid URL. Aborting the action.`,
    );
  }
  let host = parseHostHeader(headers);

  if (originDomain && (!host || originDomain !== host.value)) {
    if (!isAllowedOrigin(originDomain, allowedActionOrigins)) {
      if (host) {
        // This seems to be an CSRF attack. We should not proceed with the action.
        throw new Error(
          `${host.type} header does not match \`origin\` header from a forwarded ` +
            `action request. Aborting the action.`,
        );
      } else {
        // This is an attack. We should not proceed with the action.
        throw new Error(
          "`x-forwarded-host` or `host` headers are not provided. One of these " +
            "is needed to compare the `origin` header from a forwarded action " +
            "request. Aborting the action.",
        );
      }
    }
  }
}

// Implementation of micromatch by Next.js https://github.com/vercel/next.js/blob/ea927b583d24f42e538001bf13370e38c91d17bf/packages/next/src/server/app-render/csrf-protection.ts#L6

Review comment (suggestion):
```suggestion
      `\`origin\` header is not a valid URL. Aborting the action.`,
```

Code after:
export function throwIfPotentialCSRFAttack(
  headers: Headers,
  allowedActionOrigins: string[] | undefined,
) {
  let originHeader = headers.get("origin");
  let originDomain: string | null = null;

  try {
    originDomain =
      typeof originHeader === "string" && originHeader !== "null"
        ? new URL(originHeader).host
        : originHeader;
  } catch {
    throw new Error(
      `\`origin\` header is not a valid URL. Aborting the action.`,
    );
  }
  let host = parseHostHeader(headers);

  if (originDomain && (!host || originDomain !== host.value)) {
    if (!isAllowedOrigin(originDomain, allowedActionOrigins)) {
      if (host) {
        // This seems to be an CSRF attack. We should not proceed with the action.
        throw new Error(
          `${host.type} header does not match \`origin\` header from a forwarded ` +
            `action request. Aborting the action.`,
        );
      } else {
        // This is an attack. We should not proceed with the action.
        throw new Error(
          "`x-forwarded-host` or `host` headers are not provided. One of these " +
            "is needed to compare the `origin` header from a forwarded action " +
            "request. Aborting the action.",
        );
      }
    }
  }
}

// Implementation of micromatch by Next.js https://github.com/vercel/next.js/blob/ea927b583d24f42e538001bf13370e38c91d17bf/packages/next/src/server/app-render/csrf-protection.ts#L6
function matchWildcardDomain(domain: string, pattern: string) {