eproduction steps:
- Extract a clean station-v0.71 tarball, sys.path.insert(0, "workbench").
- import ed25519_pure as ed; from primitives import approval_router as AR
- Generate two independent Ed25519 keypairs with os.urandom(32) +
ed.publickey_from_seed(seed): one representing the target approver's
station, one representing a total stranger who has never interacted
with that station before.
- As the stranger, build an approval_request with
AR.build_request(requester_pubkey=<stranger's own pubkey>,
approver_pubkey=<target approver's pubkey>, action={"command_id":
"stripe.create_refund", "inputs": {"amount_cents": 9999900}},
reason_for_escalation="URGENT: customer escalation, approve
immediately"). Sign it with the stranger's own seed -- a completely
genuine signature, since they are only vouching for themselves.
- Deliver it exactly the way routes/relay.py's kind-dispatcher does for
kind="approval_request": AR.receive_incoming(target_ws, {"kind":
"approval_request", "body": forged_request}).
- Check the target station's pending-approval queue with
AR.list_pending(target_ws).
Expected: a station should only accept an escalation request into its
local human-facing queue from a sender it has some established
relationship with (team membership, a configured allowlist, or similar) --
not from an arbitrary, never-seen-before keypair.
Actual:
receive_incoming() accepted the request from a total stranger: True
pending queue entries before/after: 0 -> 1
The crafted request -- including an attacker-chosen action, dollar amount,
and urgency-framed reason_for_escalation text -- lands directly in the
target station's WS/approval_pending/ directory and renders in Studio's
approval UI exactly like a legitimate internal escalation, with nothing to
distinguish it as coming from an unrelated, arbitrary sender.
Root cause: workbench/primitives/approval_router.py, receive_incoming()
(~line 202-222):
def receive_incoming(ws, event):
body = event.get("body") or {}
if body.get("schema") != REQUEST_SCHEMA:
return True
requester_pubkey = body.get("requester_pubkey") or ""
ok, note = _verify(body, requester_pubkey)
if not ok:
return True
p = os.path.join(_pending_dir(ws), body["request_id"] + ".json")
...
Exactly like the decision-forgery issue, _verify() genuinely proves the
document was signed by whoever holds the keypair it names -- the defect is
that nothing constrains WHO is allowed to be that signer in the first
place. A sender can always successfully "prove" they are themselves; the
missing check is authorization (is this sender someone this station should
be listening to at all), not authentication. There is no team-membership
check, no configured allowlist, and no rate limit on how many such
requests one unrelated sender can inject.
Suggested fix: before filing an incoming request into the local pending
queue, check the requester_pubkey against an explicit allowlist or
existing relationship (e.g. team membership, matching the Teams mesh's own
membership checks) and reject/quarantine requests from unrecognized
senders rather than filing them for human review indistinguishably from
legitimate ones. At minimum, surface the sender's trust status in the
Studio approval UI so a reviewer can tell an unvetted external request
apart from an internal one.