DET-BETA-002 — preview screen scraping / automated traversal
Rule ID: DET-BETA-002
Title: Automated traversal of beta preview screens — sequential rapid-fire GET requests on /api/beta/preview/<token>/screen/*
Category: beta
Last validated: 2026-06-12 (beta-launch campaign)
State: live — beta preview endpoints active in prod. Manual query path until Heroku drain is wired.
Why this detection exists
The beta preview flow is designed as a human-paced 5-screen walkthrough. Each screen requires reading (40–120 seconds of human engagement) and a deliberate POST before the next screen unlocks. An automated scraper that harvests all five screens for their motive copy, screenshot IDs, and rubric questions would:
- Extract NDA-bound intellectual property (the motive copy and feature framing is pre-launch strategy language).
- Potentially submit synthetic rubric responses, contaminating the feedback dataset used to calibrate messaging.
- Probe the API surface for injection or state-machine bypass (e.g., can you GET screen/5 without POSTing through screens 1–4?).
The screen-lock enforcement in beta_preview.py (sequential access check via screen_completed_max) provides structural protection against skip-ahead. This detection confirms the structural control is firing correctly and catches a scraper that completes each screen legitimately but at machine speed.
Telemetry source
- Raptor app logs: GET
/api/beta/preview/<token>/screen/<N>— one request per screen. - Raptor app logs: POST
/api/beta/preview/<token>/screen/<N>— one submission per screen. - HTTP router logs: response times on screen endpoints. A human-paced flow produces inter-request gaps of 30–300 seconds. A scraper produces inter-request gaps of < 2 seconds.
- Sentry (once
sentry_backendis on): any 409 response from the screen-lock guard (screen_lockederror) surfaces as a route event.
Telemetry gap: same as DET-BETA-001 — Heroku Logplex drain not wired. Manual query required until P1 prerequisite from the June 4 catalog is met.
Statistical method + baseline window
- Primary method: time-series of inter-request gaps between screen GET and the subsequent screen POST for the same
<token>. Compare against the empirical distribution of human-paced sessions. - Baseline window: no historical baseline for this surface (first live day). Use absolute floor thresholds (defined below) until 7 days of tester sessions build a distribution.
- Secondary method: sequence-anomaly detection — does the token exhibit requests for screen N before POSTing screen N-1? The route enforces the lock structurally (409), but a scraper that deliberately steps backward is observable in the 409 sequence.
- K-S test (post-launch): once 50+ tester sessions complete, run monthly K-S test comparing inter-request gap distribution for recent cohort vs. baseline cohort. A shift toward shorter gaps (D-statistic > 0.2 at alpha=0.05) indicates more automated traversal in the recent cohort.
Threshold + expected FP rate
- Absolute trigger (scraper signature): any token that completes the sequence GET screen/1 → POST screen/1 → ... → POST screen/5 in under 90 seconds total. Human completion rate expected: 8–25 minutes minimum given reading engagement.
- Soft trigger (speed concern): any token that traverses from screen/1 GET to screen/5 POST in under 5 minutes. Logs to MEDIUM; requires manual review.
-
409-rate trigger: >= 3
screen_locked(409) responses for the same token within 5 minutes = active skip-ahead probe. -
Expected FP rate: near-zero. A human completing all 5 screens in under 90 seconds is not physically plausible given the rubric on screen 5 alone (14 fields). The 5-minute soft trigger has moderate FP risk: a motivated tester who skims rather than reads and already has formed opinions could complete in 4–6 minutes. Tag these as
fast-humanand log without alarming.
Alert route
- HIGH (sub-90-second completion from any token):
#raxx-ops-alert-sev2-5(ET) /#raxx-ops-alert-sev2(off-hours). Per-event — contamination of rubric dataset is immediate feedback-quality concern. - MEDIUM (sub-5-minute completion, or >= 3 screen-lock 409s): ops@ daily digest.
- LOW (inter-request gap < 5s on any single screen transition without triggering full-session flag):
docs/detections/_log/silent log.
Escalation owner
- operator — rubric dataset contamination decision. Operator decides whether to invalidate suspicious tester sessions from the feedback aggregate. Detection provides the token + timing fingerprint.
- security-agent — if the scraper appears to be probing for state-machine bypass (many 409s, boundary-value screen numbers) rather than just harvesting content. State-machine injection is a code-level concern.
Test fixture / synthetic positive
See _fixtures/preview_screen_scraping_positive.json — a synthetic session for token hash synth-scraper-token-001 completing all 5 screens in 34 seconds, with inter-request gaps of 0.8–2.1 seconds.
Manual query (until Heroku drain exists)
# Identify all token slugs that appear in logs with screen requests,
# then compute time from first GET /screen/1 to last POST /screen/5
heroku logs --app raxx-api-prod --num 3000 \
| grep 'beta/preview' \
| grep -E '/screen/' \
| awk '{print $1, $7}' \
| sort -k2
# Manually compute per-token time spans from first to last request.
What NOT to do
- Do not auto-invalidate the tester's rubric data from a detection fire alone. That is an operator data-quality decision, not a detection action.
- Do not use the 409 screen-lock responses as the only signal — those indicate a skip-ahead attempt, not necessarily scraping (a confused human could hit the back button and retry a screen).
- Do not tighten the 90-second threshold before collecting 50+ legitimate sessions to establish the empirical minimum.