CVE-2026-48797: backpropagate Authentication Bypass on UI Training Control Plane
The backpropagate web UI ignores the --auth flag operators pass at startup, so anyone who can reach the bound port gets full, unauthenticated access to training controls, dataset uploads, and HuggingF
The problem
In backpropagate 1.1.0 and 1.1.1, the CLI accepts --auth user:pass and prints a confirmation that authentication is enabled. It exports BACKPROPAGATE_UI_AUTH to the Reflex subprocess, but the Reflex backend never reads that variable. No HTTP middleware and no WebSocket upgrade guard is registered anywhere in the ui_app codebase.
The gap was documented internally in a source comment at cli.py:1217-1218 ('For Phase 1 the variable is exported but Reflex doesn't read it yet'), while the user-facing README, CHANGELOG, and SHIP_GATE documents all described the --auth contract as enforced. Operators who added --share expecting a protected public URL had no authentication layer at all.
Proof of concept
# Operator starts the UI believing auth is active:
# backprop ui --auth admin:secret --share
#
# Attacker hits the port with no credentials.
# The Reflex backend returns 200 and full UI access on every route:
curl -s http://<target>:7860/
# Returns the full Reflex app HTML -- no 401, no WWW-Authenticate header.
# Trigger a training run via the /_event WebSocket (Reflex state event bus):
# wscat -c ws://<target>:8000/_event
# {"type": "event", "name": "TrainState.start_training", "payload": {}}
#
# Upload arbitrary files via the unauthenticated rx.upload endpoint:
curl -F 'file=@evil.jsonl' http://<target>:8000/upload/
# No size cap, no extension filter, no session check in v1.1.x.The root cause is CWE-358 (Improperly Implemented Security Check): the CLI performs a security action (printing 'Auth: enabled') and sets an env var, but the component that must enforce the check (the Reflex ASGI app) was never wired to read it. This is a classic fail-open from an incomplete feature shipped as complete.
Because Reflex's default app has no middleware at all, every HTTP route and WebSocket upgrade proceeds without any credential check. The rx.upload endpoint compounds the issue by also lacking file-size caps and extension filtering, enabling disk-fill DoS alongside the auth bypass.
The patch in v1.2.0 introduces real ASGI middleware via rx.App(api_transformer=basic_auth_transformer) that validates credentials before HTTP responses AND before websocket.accept() is called on the /_event upgrade, so the guard cannot be bypassed by going directly to the WebSocket.
The fix
Upgrade to backpropagate 1.2.0 (released 2026-05-23). Run: pip install --upgrade backpropagate. The patch adds a four-mode ASGI auth layer (no_auth_local_only, token_auto, explicit_creds, production) with HMAC-signed cookies, Host and Origin allowlists, and env-var stripping so direct 'python -m reflex run' invocations also enforce authentication.
If you cannot upgrade immediately, do not use --auth or --share; restrict access with SSH port-forwarding instead (ssh -L 7860:localhost:7860 <host>). Audit and rotate any HuggingFace tokens used during sessions where --share was active.
Reported by dogfood-swarm Stage A audit (finding FRONTEND-A-001).