247 lines · 9.1 KB
import { env } from "cloudflare:workers";
import { all, first, run } from "./db";
import { hmacSha256Hex, randomId } from "./crypto";
import { badRequest, notFoundError } from "./errors";
import { cookieSecret, publishMeta, SCRATCH_LIMITS, sitesDomain } from "./sites/config";
import { loadSiteById } from "./sites/deploy";
import { hostLabel } from "./sites/naming";
import type { AbuseAction, AbuseCategory, AbuseStatus } from "~/lib/abuse";

export type AbuseSource = "web" | "email" | "site";

/** Phishing and malware hurt visitors immediately, so fewer distinct reporters are enough to hold the site. */
const SEVERE: ReadonlySet<AbuseCategory> = new Set(["phishing", "malware"]);
const PAUSE_THRESHOLDS = {
  scratch: { severe: 2, normal: SCRATCH_LIMITS.reportsToDisable },
  owned: { severe: 3, normal: 5 },
} as const;
const PAUSE_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
export const PAUSE_REASON = "Paused pending abuse review";
const MAX_DETAILS = 4000;
const MAX_URL = 2000;

export type AbuseReportInput = {
  source: AbuseSource;
  targetUrl: string;
  category: AbuseCategory;
  details: string;
  reporterEmail: string | null;
  /** Raw IP or sender address; only its HMAC is stored. */
  reporterKey: string;
  /** Known when the report came from the site itself, so the URL need not be resolved. */
  siteId?: string;
};

export type AbuseReportRow = {
  id: string;
  source: AbuseSource;
  target_url: string;
  site_id: string | null;
  category: string;