88 lines · 4.3 KB
import { handleAttachmentRoutes } from "./attachments";
import { viewerFromSessionCookie } from "./auth";
import { handleAvatarRoutes } from "./avatars";
import { handleCamoRoutes } from "./camo";
import { handleRawRoutes } from "./raw";
import { userContentUrl } from "./usercontent-grant";
import { forgeOriginFor, isUserContentOrigin, userContentOriginFor } from "./usercontent-origin";
import { isPublicUserContentPath, isUserContentPath, USERCONTENT_GRANT_PARAM } from "./usercontent-policy";

const READ_METHODS = new Set(["GET", "HEAD"]);
const PUBLIC_HOP_CACHE = "public, max-age=3600";

/** Headers every user-content response carries, whatever the handler set: no sniffing, no scripts, no cookies. */
function harden(response: Response, forgeOrigin: string): Response {
  const headers = new Headers(response.headers);
  headers.delete("set-cookie");
  headers.set("x-content-type-options", "nosniff");
  if (!headers.has("content-security-policy")) headers.set("content-security-policy", "default-src 'none'; sandbox");
  headers.set("referrer-policy", "no-referrer");
  headers.set("cross-origin-resource-policy", "cross-origin");
  headers.set("access-control-allow-origin", forgeOrigin);
  headers.set("vary", [headers.get("vary"), "Origin"].filter(Boolean).join(", "));
  return new Response(response.body, { status: response.status, statusText: response.statusText, headers });
}

/**
 * Requests on the user-content origin: only the read-only serving routes exist there. Anything else (pages,
 * server functions, uploads, APIs) is a 404, so a payload that lands on this origin finds nothing to call.
 */
export async function handleUserContentOrigin(request: Request, url: URL): Promise<Response | null> {
  if (!isUserContentOrigin(url)) return null;
  const forgeOrigin = forgeOriginFor(url);
  if (request.method === "OPTIONS") {
    return new Response(null, {
      status: 204,
      headers: {
        "access-control-allow-origin": forgeOrigin,
        "access-control-allow-methods": "GET, HEAD",
        "access-control-max-age": "86400",
      },