v0.5.14: bilateral-required `wire add` (security)
Security release — closes phonebook-scrape pairing vulnerability
Affected versions: v0.5.9 through v0.5.13 (all v0.5.x releases that shipped the zero-paste wire add handle flow).
Severity: Medium. Unauthenticated remote write capability to any wire user's slot via public phonebook enumeration. No remote code execution; no impersonation (Ed25519 sigs still verify); no inbox read access.
The bug
v0.5.13's receiver-side maybe_consume_pair_drop (src/pair_invite.rs:489-548) auto-pinned a stranger's signed pair_drop event with VERIFIED tier and shipped back pair_drop_ack carrying the receiver's slot_token — the bearer credential for authenticated writes to that slot. A malicious actor could:
- Scrape
GET /v1/handlesand/phonebook(public by design — Mastodon-style discovery). - For each handle: POST a signed pair_drop to the auth-free
/v1/handle/intro/<nick>endpoint. - Receive the victim's
slot_tokenviapair_drop_ack. - Send arbitrary signed events (any kind, up to 256 KiB per event, 64 MB slot quota) to the victim's inbox.
Cost: 1 keypair, ~1 hour of scripting. No defense beyond the relay's global rate limiter and slot quota.
The fix — bilateral-required wire add
wire add must now fire on both sides before any capability flows. The design intent — bilateral consent — was never quite implemented for the zero-paste handle flow; this release restores it.
Receiver-side gate. maybe_consume_pair_drop is bifurcated on nonce presence:
- SPAKE2 invite-URL path (pair_drop carries pre-shared
pair_nonce): unchanged. The invite-URL nonce IS the consent gesture. - Handle path (no nonce, zero-paste
wire add): write tostate/wire/pending-inbound-pairs/<handle>.json. No trust pin. No ack. No slot_token leak. OS toast prompts operator to runwire add <peer>@<their-relay>to accept orwire pair-reject <peer>to refuse.
Operator-side completion. cmd_add checks pending-inbound first. If present → bilateral completion: pin VERIFIED, ship slot_token via pair_drop_ack, delete pending record. Otherwise → outbound (unchanged) — emit signed pair_drop, await reciprocal wire add from peer.
Attack surface after v0.5.14
A phonebook-scrape + spray produces N records in N victims' pending-inbound queues. Zero VERIFIED pins, zero slot_token leaks, zero write capability. Each victim sees one OS toast per attacker. Victims who don't manually wire add back are fully protected.
Zero-paste UX preserved: two commands (wire add from each side).
New CLI surface
wire pair-list-inbound [--json] # enumerate pending-inbound (flat array)
wire pair-reject <peer> # drop a pending record without pairing
wire pair-list # human output: PENDING INBOUND section + SPAKE2
wire status # JSON: pending_pairs.inbound_count + inbound_handleswire pair-list --json shape unchanged for back-compat with existing scripts.
Tests
145 lib + 28 CLI tests passing. Four new pending-inbound integration tests in tests/cli.rs.
Upgrade
cargo install slancha-wire
wire upgrade # respawns daemon at v0.5.14No protocol or schema changes. Existing verified peers remain paired.
Credit + disclosure
Vulnerability identified during pre-release security review on 2026-05-17. No known exploitation in the wild. Patch released same day. Full code-path analysis + four-persona threat-model review preceded the fix.
Full Changelog: v0.5.13...v0.5.14
Full Changelog: v0.5.13...v0.5.14