What
frontends/conductor.py adds a RemoteAuth middleware (lines 124-135, commit 6bed654), but the gate at line 130 is a 3-way AND — if args.key and remote not in ("127.0.0.1", "::1") and not secrets.compare_digest(got, want) — so it only fires when --key is set AND the caller is off-loopback AND the header mismatches. Two holes fall out of that: loopback callers are always exempt regardless of --key, and --key has no default (line 33), so python frontends/conductor.py starts on 127.0.0.1:8900 with no auth on any route. The same app installs CORSMiddleware(allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]) at line 122, so a cross-origin fetch() from any page the user visits passes preflight and reaches POST /subagent (591), POST /subagent/{sid}?action=input (597), POST /approval (632), GET /subagent (576).
How to reproduce
# Terminal 1 — default launch (loopback, no --key)
python frontends/conductor.py --no-browser &
# Terminal 2 — no Authorization header; this is what a drive-by fetch() delivers
NONCE=$(date +%s)
curl -s -o /dev/null -w '%{http_code}\n' -X POST http://127.0.0.1:8900/subagent \
-H 'Content-Type: application/json' \
-d "{\"prompt\":\"canary-$NONCE: reply OK and stop\"}"
# actual: 200 (under any auth this should be 401)
curl -s http://127.0.0.1:8900/subagent | grep -o "canary-$NONCE[^\"]*"
# actual: canary-<NONCE>: reply OK and stop
The browser form is fetch("http://127.0.0.1:8900/subagent",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({prompt:"..."})}) — the wildcard CORS response satisfies the preflight and the POST is delivered.
Impact / scope
A visited web page (or any local process) can start a subagent with an attacker-authored prompt and feed it follow-up instructions via action=input, while the agent runs under the user's privileges and keys; POST /approval pushes a fake approval card to the front-end. This is cross-principal task dispatch and instruction injection into the user's agent session, not remote code execution — the agent's terminal action is model-mediated and not demonstrated end-to-end. Direct remote reach is bounded by the loopback bind; the wildcard CORS is what extends it to "any page open in a browser while the conductor runs." PR #650 raised the same loopback + CORS-* + unauth-route pattern on desktop_bridge.py's /path/open; it was closed with no maintainer response, and the conductor routes weren't covered.
Suggested change
Have RemoteAuth enforce for loopback too, default --key to a generated token written to a file (or refuse to start without one), and replace allow_origins=["*"] with an explicit origin allow-list. Happy to open a PR.
What
frontends/conductor.pyadds aRemoteAuthmiddleware (lines 124-135, commit6bed654), but the gate at line 130 is a 3-way AND —if args.key and remote not in ("127.0.0.1", "::1") and not secrets.compare_digest(got, want)— so it only fires when--keyis set AND the caller is off-loopback AND the header mismatches. Two holes fall out of that: loopback callers are always exempt regardless of--key, and--keyhas no default (line 33), sopython frontends/conductor.pystarts on127.0.0.1:8900with no auth on any route. The same app installsCORSMiddleware(allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])at line 122, so a cross-originfetch()from any page the user visits passes preflight and reachesPOST /subagent(591),POST /subagent/{sid}?action=input(597),POST /approval(632),GET /subagent(576).How to reproduce
The browser form is
fetch("http://127.0.0.1:8900/subagent",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({prompt:"..."})})— the wildcard CORS response satisfies the preflight and the POST is delivered.Impact / scope
A visited web page (or any local process) can start a subagent with an attacker-authored prompt and feed it follow-up instructions via
action=input, while the agent runs under the user's privileges and keys;POST /approvalpushes a fake approval card to the front-end. This is cross-principal task dispatch and instruction injection into the user's agent session, not remote code execution — the agent's terminal action is model-mediated and not demonstrated end-to-end. Direct remote reach is bounded by the loopback bind; the wildcard CORS is what extends it to "any page open in a browser while the conductor runs." PR #650 raised the same loopback + CORS-*+ unauth-route pattern ondesktop_bridge.py's/path/open; it was closed with no maintainer response, and the conductor routes weren't covered.Suggested change
Have
RemoteAuthenforce for loopback too, default--keyto a generated token written to a file (or refuse to start without one), and replaceallow_origins=["*"]with an explicit origin allow-list. Happy to open a PR.