Skip to content

Security hardening + correctness fixes: WebSocket authz, OAuth2 OIDC, file-service IDOR, macro feature-gating #19

@JedimEmO

Description

@JedimEmO

Summary

A full-workspace correctness/design review plus the open items from the 2026-06 security audit surfaced a set of bugs and hardening gaps across the WebSocket RPC stack, the service macros, the auth/identity crates, and the example templates. This issue tracks fixing all of them.

Correctness bugs (WebSocket RPC)

  • WS client connect() busy-wait + hang — spins a hot loop waiting for ConnectionEstablished with no sleep/timeout; hangs forever if the server never sends it. state is also set to Connected before the handshake completes.
  • Pending-request leakcall() doesn't remove its pending entry on timeout, so after max_pending_requests timeouts every future call fails permanently with "Too many pending requests".
  • DashMap guards held across .await in broadcast paths — a slow consumer with a full bounded channel blocks the send while holding the shard lock, deadlocking other map access (incl. the consumer's own remove_connection); fan-out is also sequential.
  • Subscription/remove_connection races — leave zombie ids in the topic index or drop freshly-added subscribers.
  • Unparseable frame kills the connection silently — a frame that parses as neither message type drops the whole connection instead of replying with JSON-RPC -32700.

Security / authorization

  • WS topic subscribe has no authz (audit HIGH-2) — default handle_subscribe subscribes to any attacker-supplied topic.
  • WS token expiry never re-validated mid-connection (audit HIGH-1) — revoked/expired credentials stay valid until disconnect.
  • require_auth defaults to false for WS services (audit MED-1).
  • REST macro parses request body before auth — Json extractor runs before the auth/CSRF/permission check; free pre-auth CPU/alloc for unauthenticated clients, and no body-size knob.
  • OAuth2: no nonce, no id_token verification, no state↔session binding (audit HIGH-3) — identity taken only from userinfo; login-CSRF possible.
  • File-service example IDOR/XSS (audit HIGH-4, template users copy) — get_file prefix-matches ids, no object-ownership check on download, reflected attacker-controlled Content-Type.
  • Unbounded in-memory stores (audit MEDIUM) — OAuth2 InMemoryStateStore and the session store grow without bound; cleanup only runs opportunistically. No WS connection cap.

Design / maintainability

  • Auth pipeline duplicated ~6× across the service macros (credential → CSRF → authenticate → OR-of-AND permissions), already diverging (file macro treated WITH_PERMISSIONS([]) as deny-all vs authenticated-only elsewhere).
  • OAuth2 StartFlow returns its success value inside Err(ProviderError) — callers must parse JSON out of an error.
  • Proc-macro cfg!(feature = ...) resolves against the macro crate, not the consumer — workspace feature unification forces client/server codegen into unrelated consumers (root cause of an earlier build break).

Tooling

  • cargo audit was not installed / not run.

Acceptance

All items fixed with regression tests; cargo test --workspace, cargo clippy --workspace --all-targets, and cargo audit green.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions