DET-BETA-004 — NDA acknowledgment bypass probe
Rule ID: DET-BETA-004
Title: Repeated 403 nda_not_acked responses on beta preview routes — NDA gate probe
Category: beta
Last validated: 2026-06-12 (beta-launch campaign)
State: live — the NDA guard runs on every request via _guard() in beta_preview.py. 403 responses with {"error": "nda_not_acked"} are emitted in the response body and logged via logger.error("beta_preview._guard db_error: %s", exc) on DB failures. Manual query path until Heroku drain wired.
Why this detection exists
The beta preview walkthrough requires NDA acknowledgment before any screen content is served. The check is:
if not _nda_acked(conn, tester["tester_email"]):
return (jsonify({"error": "nda_not_acked", "redirect_to": f"/beta/walk/{token}"}), 403)
The beta_nda_acknowledgements table is owned by Console; Raptor queries it via the Postgres connection. A 403 with nda_not_acked is the expected response for a tester who has a valid token but has not completed the NDA flow.
Two distinct adversarial patterns produce 403 clusters here:
-
Token-valid, NDA-bypassed scraper. An actor who obtained a valid token (from a forwarded invite, a tester who shared their link) attempts to access screen content without completing the NDA flow. They receive 403s and may retry with different paths, user-agents, or methods looking for a bypass. This is distinct from the enumeration signature in DET-BETA-001 (where the token itself is invalid).
-
NDA table unavailability probe. If
beta_nda_acknowledgementsis inaccessible (DB error),_guard()logs an error and returns 500. An actor who can trigger DB errors at the NDA query layer (e.g., via SQL-injection probing of thetester_emailparameter passed by token verification) gets a different error code. A cluster of mixed 403/500 responses on the same token is a DB-layer probe signature.
The structural protection is correct — the guard fires. This detection confirms the guard is firing and surfaces sustained probe campaigns.
Telemetry source
- Raptor app logs: HTTP router log entries with
status=403on paths matchingbeta/preview. - Raptor app logs:
beta_preview._guard db_errorlog entries (indicates NDA table query failed — different from NDA-not-acked). - Sentry (once
sentry_backendon): DB-error exceptions from_guard()surface automatically.
Telemetry gap: Heroku drain not wired; manual query required.
Statistical method + baseline window
- Method: Poisson tail on 403-responses-per-token per rolling 5-minute window. Secondary: count of distinct tokens exhibiting 403 within a 30-minute window (cardinality drift on affected-token set).
- Baseline window: rolling 7 days. Pre-baseline: absolute floor.
- Key distinction from DET-BETA-001: DET-BETA-001 fires on 401 (invalid token — token doesn't verify). DET-BETA-004 fires on 403 (valid token, NDA gate blocking). These are different seams and different attack patterns.
Threshold + expected FP rate
- Expected 403 rate from legitimate testers: every tester hits exactly one 403 if they follow the invite link before completing the NDA flow. The NDA flow is step-1 of the walkthrough; a well-designed invite email links to the NDA step first. If the email links directly to
/preview/<token>/screen/1, a tester gets one 403, redirects to/beta/walk/<token>, completes NDA, and is never 403'd again. Baseline expected: 0–1 403 per legitimate tester. - Trigger (probe signature): >= 5 403 responses for the same token within 10 minutes.
- Trigger (multi-token NDA bypass campaign): >= 10 distinct tokens exhibiting at least one 403 within 30 minutes. This suggests a scraper with multiple valid tokens attempting to bypass the NDA gate systematically.
- Expected FP rate: near-zero for the per-token threshold (no legitimate tester retries 5 times after seeing a 403 that includes a redirect hint). Moderate for the multi-token threshold — if beta invites go out in a batch at the same time, many testers will hit the NDA gate simultaneously before completing the flow. Suppress the multi-token trigger for the first 2 hours after a batch send by correlating with the
beta_nda_acknowledgementstable row-creation velocity.
Alert route
- HIGH (>= 5 403s per token in 10 min, or >= 10 affected tokens in 30 min):
#raxx-ops-alert-sev2-5(ET) /#raxx-ops-alert-sev2(off-hours). Per-event — active bypass probe or NDA table outage both warrant same-hour response. - MEDIUM (2–4 403s per token in 10 min, pattern not conclusive): ops@ daily digest.
- LOW (single 403 per token, no retry pattern): silent — this is the expected invite-arrival flow.
Escalation owner
- security-agent — if 403 cluster on one token is accompanied by unusual request methods or paths (e.g.,
PUT /screen/1,DELETE /screen/1) that indicate active probe beyond simple retry. The probe may be looking for a bypass in a less-tested HTTP method. - sre-agent — if 403s are accompanied by
db_errorlog lines. This indicates thebeta_nda_acknowledgementstable is inaccessible, which is an ops incident (not a security probe). - operator — if a batch of invites went out simultaneously and the multi-token trigger fires in the first 2 hours. Review the NDA-acknowledgment flow UX; testers may not be reaching the NDA step before the preview link.
Invited-batch send correlation
When a batch of beta invites is sent, note the approximate UTC time. For the following 2 hours, suppress multi-token 403 alerts (they are expected — testers just received the link). Single-token bursts (>= 5 per token in 10 min) remain live.
This suppression is manual today (operator notes the send time; detection-engineer reviews the alert window). Automate once the drain is wired.
Test fixture / synthetic positive
See _fixtures/preview_nda_bypass_probe_positive.json — 7 synthetic 403 responses for token hash synth-nda-probe-token-001 (synth-tester-01@example.test) within 9 minutes, with a mix of screen endpoints probed sequentially, suggesting a scraper cycling through screens after receiving the first 403.
Manual query (until Heroku drain exists)
heroku logs --app raxx-api-prod --num 2000 \
| grep 'beta/preview' \
| grep 'status=403'
Group by the token slug in the URL path to identify per-token 403 clusters.
What NOT to do
- Do not invalidate a tester's token on a 403 burst alone — the 403 is a structural gate doing its job. Invalidation is an operator decision once the probe is confirmed.
- Do not conflate this detection with DET-BETA-001 (invalid token enumeration). A 403 means the token is valid but NDA is incomplete. The distinction matters for escalation: DET-BETA-001 → security-agent; DET-BETA-004 → operator first, security-agent only if bypass-pattern is confirmed.