Skip to content

Fraud detection for leaderboard & token abuse - #78

Open
QuarkOS wants to merge 3 commits into
Birdabo404:mainfrom
QuarkOS:cursor/fraud-detection-leaderboard-e229
Open

Fraud detection for leaderboard & token abuse#78
QuarkOS wants to merge 3 commits into
Birdabo404:mainfrom
QuarkOS:cursor/fraud-detection-leaderboard-e229

Conversation

@QuarkOS

@QuarkOS QuarkOS commented Aug 28, 2026

Copy link
Copy Markdown

What & why

The ingest routes already enforce hard bounds (per-event duration caps, a rolling 24h active-time/visit ceiling, a domain allowlist, per-record token caps, freshness windows). Those stop the crudest inflation, but a patient scripted extension can sit just under every cap and climb the board on fabricated activity, and a client can backfill impossible token totals onto the Burn Board.

This PR adds the missing detection layer: it runs over what actually landed in the database, looks for the behavioural signatures the hard caps can't express, raises deduped flags, alerts staff, and gives staff a review queue to confirm (optionally suspending) or dismiss.

How it works

Detection engine — src/lib/fraudDetection.ts (pure, deterministic)
Seven evidence-based detectors, biased for precision (each carries the raw numbers it fired on, for audit):

Signal Surface Catches
activity_ceiling_pinning activity Banking >=90% of the 16h/day active ceiling day after day
impossible_concurrency activity Verified active time far exceeding the day's wall-clock span (parallel forged streams)
visit_flooding activity Near-ceiling visits with negligible active time (flat 40-pt visit padding)
uniform_duration_padding activity One duration value dominating a large heartbeat sample (scripted client)
token_impossible_rate token Sustained billions of tokens/day — not a real agent workload
token_spike token A single day dwarfing the user's own median (backfilled dump)
token_cost_mismatch token Huge token totals with implausible cost/token (gaming the cost-ordered Burn Board)

assessUserFraud rolls signals up per category into a capped 0–100 risk score, a level band, and a stable dedupe fingerprint.

Persistence & orchestration — src/lib/fraudDetectionServer.ts + migration 061

  • fraud_flags table (service-role only): one row per (user, category), deduped by fingerprint, with open/confirmed/dismissed triage state and an audit-friendly signal snapshot.
  • The service layer fetches a user's recent activity + token history, assesses it, upserts flags (a recurring pattern refreshes the row and bumps detection_count without resurrecting a staff decision), and alerts every staff member once per newly-opened flag. Everything is best-effort and never throws.
  • sweepFraudSignals scans the competitive-board candidate set (top scorers + Burn Board opt-ins) and piggybacks the existing leaderboard-integrity cron (same Vercel cron-budget reason as the sponsor sweep).

Staff surface

  • abuse.review staff action at the moderator floor.
  • GET /api/admin/abuse — worst-first queue with per-status counts and the flagged account's identity.
  • POST /api/admin/abuse/[id]/review — confirm/dismiss (audit-first, target-guarded, status-guarded, reason required), with optional suspend on confirm via the shared moderation path.
  • /admin/abuse review-queue page (mirrors the Teams queue) + a nav entry under Review.

Testing

  • 31 new unit tests (engine + server layer with a mock Supabase client). Full suite: 990 passing (was 959).
  • typecheck, lint, audit:prod (0 vulns), and next build all green; new routes compile (/admin/abuse, /api/admin/abuse, /api/admin/abuse/[id]/review).
  • Ran the real engine over synthetic histories — a heavy-but-human user is CLEAN (no false positives), while an activity cheater and a token cheater both hit critical risk with the expected signals:
=== Clean heavy-but-human user ===
  verdict: CLEAN — no signals fired

=== Activity cheater (scripted extension) ===
  [activity] level=critical risk=100/100
     - (high, +58) activity_ceiling_pinning: Banked >=90% of the 16h/day active-time ceiling on 6 day(s)
     - (high, +55) impossible_concurrency: Active time exceeded the wall-clock window 2.50x — parallel activity streams

=== Token cheater (fabricated Burn Board) ===
  [token] level=critical risk=100/100
     - (high, +50) token_impossible_rate: Reported >=2,000,000,000 tokens/day on 2 day(s)
     - (high, +35) token_spike: One day reported 2609x the median token day — backfilled spike
     - (medium, +20) token_cost_mismatch: Reported 5,804,200,000 tokens for $12.00 — implausible cost/token

A screenshot of the review-queue UI (rendered with the real admin component kit) is attached to the companion PR on the fork. Live admin-panel interaction needs an authenticated staff session against a real Supabase, so the queue was rendered via a temporary preview route (removed before commit). The detection engine — the novel, high-risk part — is proven end-to-end by unit tests and the runtime demo above.

Deploy notes

  • Apply migrations/061_fraud_flags.sql (creates the table; ends with NOTIFY pgrst). Thresholds live in DEFAULT_FRAUD_POLICY and can be tuned without schema changes.
  • No new cron entry needed — the sweep rides the existing /api/cron/leaderboard-integrity schedule.

Possible follow-ups (not in this PR)

  • Cross-account device-sharing detection (multiple accounts on one device_uuid).
  • A fraud-flag panel on the per-user admin dossier.
  • Auto-exclusion of confirmed accounts from the canonical leaderboard ranker.

Opened from fork QuarkOS/Cribble (branch cursor/fraud-detection-leaderboard-e229).

cursoragent and others added 3 commits August 27, 2026 23:10
Detects behavioural signatures that the ingest hard caps cannot express:
activity-ceiling pinning, physically impossible concurrency, visit-bonus
flooding, scripted uniform durations, impossible token throughput,
backfilled token spikes, and cost/token mismatch. Every detector is a pure,
deterministic function over events_raw + agent_usage_daily rows, biased for
precision (each carries its raw evidence). assessUserFraud rolls signals up
per category with a capped 0-100 risk score, level band, and a stable dedupe
fingerprint. Covered by 22 unit tests.

Co-authored-by: Emilio Schwaiger <QuarkOS@users.noreply.github.com>
Migration 061 creates the service-role-only fraud_flags table (one row per
user+category, deduped by a signal fingerprint, with open/confirmed/dismissed
triage state and an audit-friendly signals snapshot). fraudDetectionServer
fetches a user's recent activity + token history, runs the pure assessor,
upserts flags (refreshing recurring patterns without resurrecting a staff
decision), and alerts staff once per newly-opened flag. sweepFraudSignals
assesses the competitive-board candidate set for the cron. All best-effort and
never-throwing; covered by 9 unit tests with a mock Supabase client.

Co-authored-by: Emilio Schwaiger <QuarkOS@users.noreply.github.com>
The leaderboard-integrity cron now piggybacks sweepFraudSignals (same Vercel
cron-budget reason as the sponsor sweep) and reports the result. Adds an
abuse.review staff action at the moderator floor, the /api/admin/abuse list
(worst-first, with per-status counts and the flagged account's identity) and
/api/admin/abuse/[id]/review confirm/dismiss endpoint (audit-first, target-
guarded, status-guarded, with optional suspend on confirm via the shared
moderation path). Surfaces a new /admin/abuse review-queue page mirroring the
teams queue, with a nav entry under Review.

Co-authored-by: Emilio Schwaiger <QuarkOS@users.noreply.github.com>
@vercel

vercel Bot commented Aug 28, 2026

Copy link
Copy Markdown

@cursoragent is attempting to deploy a commit to the BIRDABO Team on Vercel.

A member of the Team first needs to authorize it.

@QuarkOS
QuarkOS marked this pull request as ready for review August 28, 2026 12:35
Copilot AI lite review requested due to automatic review settings August 28, 2026 12:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Birdabo404 added a commit that referenced this pull request Aug 29, 2026
…ing: scoring v3 paid the flat 40-point bonus for every visit row inside a session, so re-focusing/reloading one tool tab every few seconds minted a bonus per flap while sailing under the 600/day ceiling — observed in production (Aug 2026) as a reload loop banking ~250 same-domain visits a day (~10k pts/day, 69% of the account's total), enough to reach #2 on a five-day-old account. A session already models one continuous same-domain engagement, so v4 pays it at most one visit bonus (sessionVisitCap) — returning after the 5-minute gap opens a new session and earns normally — and the ingest ceiling drops 600 -> 150, calibrated against the heaviest organic day on record (84 visits): honest use is untouched while a maxed-out visit farm now earns less than a genuine heavy day of active time. Repricing is retroactive: scores rebuild from events_raw on the next sync or a verify-score-parity --fix pass, so farmed history deflates without touching accounts or rows. Credit to @QuarkOS (PR #78) for flagging that the ingest hard caps bound volume but never verify authenticity; this lands the structural fix on the scoring side — depricing the exploit beats detecting it after it pays.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants