Skip to content

Resume replays envelopes from every session, leaking cross-session data #44

Description

@nficano

src/Internal/Runtime/LifecycleHandler.php:118 handles a Resume envelope by calling EventLog::replayAfter($after) and forwarding every yielded envelope onto the requesting session's transport. The underlying query at src/Store/EventLog.php:151 is SELECT payload_json FROM events WHERE rowid > :rowid ORDER BY rowid ASC and is not constrained by session_id, principal, or any other ownership column. When $msg->afterMessageId is null or an empty string the loop replays the entire append-only event log from the beginning, including envelopes that were recorded for other sessions.

A peer can therefore send Resume(after_message_id: "") and harvest every event ever logged across all sessions — tool inputs, telemetry, human prompts, granted leases, error payloads — even when those events belong to a different principal. The runtime advertises Resume as a session-scoped reconnection primitive in docs/guides/resume.md:29, and the auth invariant statement there explicitly promises "Only resume sessions under the same authenticated principal." The current code does not enforce that invariant. The events table already carries a session_id column and an events_session_idx index, so filtering by the requesting session is cheap.

Fix prompt: Scope the resume replay to envelopes the requesting session is entitled to see. In LifecycleHandler::handleResume, pass the calling Session's id and principal to a new EventLog::replayAfterForSession() (or add a session filter parameter to replayAfter()) and emit DATA_LOSS / PERMISSION_DENIED for cross-session after_message_id values. Add an integration test in tests/Integration/ResumeTest.php that opens two sessions, records events on each, and asserts that a Resume(after_message_id: "") on session A receives only envelopes from session A.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingseverity:highHigh severity

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions