28 lines · 1.1 KB
/** Pure EDITOR_ORIGIN validation (no Worker bindings), so it unit-tests under bun. */

function within(host: string, domain: string | undefined): boolean {
  const base = (domain ?? "")
    .trim()
    .toLowerCase()
    .replace(/^\.+|\.+$/g, "");
  return base !== "" && (host === base || host.endsWith(`.${base}`));
}

/**
 * Why an EDITOR_ORIGIN is unsafe, or null. It must be its own host: never the forge host (cookies and
 * pages), and never under SITES_DOMAIN, where user content could frame or impersonate it.
 */
export function editorOriginProblem(raw: string, forgeHost: string, domains: { sites?: string }): string | null {
  let origin: URL;
  try {
    origin = new URL(raw);
  } catch {
    return "EDITOR_ORIGIN is not a valid URL";
  }
  const host = origin.hostname.toLowerCase();
  if (host === forgeHost.toLowerCase()) return "EDITOR_ORIGIN must not be the forge host";
  if (within(host, domains.sites)) return "EDITOR_ORIGIN must not be under SITES_DOMAIN (user content)";
  if (origin.protocol !== "https:" && !within(host, "localhost")) return "EDITOR_ORIGIN must use https";
  return null;
}