Skip to content

id(world-replication): replication stream serves the full world journal to any peer — no read authorization before the feed opens (PR #4589) #4723

Open

Description

@dezren39

Finding (from PR review, never addressed)

World journal replication serves a world's complete journal to any Iroh peer that supplies a nonzero session nonce — no capability, membership, or admin credential is checked before the journal is opened and streamed. Omitting world even selects the default world. This bypasses the capability validation used for the existing records replication path and exposes journaled chat, inputs, and host events to nonmembers.

Raised independently by two reviewers on PR #4589:

  • Codex (P1), 2026-11-02T21:31:55Z: feat(id): serve signed world journal replication (#806 phase 2) #4589 (comment) — "Authorize replication before exposing the journal" (pkgs/id/src/world_replication_net.rs:114)
  • CodeRabbit (Major, Security & Privacy), 2026-11-02T21:37:25Z: feat(id): serve signed world journal replication (#806 phase 2) #4589 (comment) — after ensure_nonce, serve_stream calls hub.lease(request.world.as_deref(), None) and replication_feed(); neither checks the peer's identity or credentials; the nonce only rejects an all-zero value; REPLICATION_ALPN is also registered without an access policy (pkgs/id/src/commands/serve.rs:1145-1157). The participant projection hides chat and zeroes seeds for /id-world/1, but this stream sends raw journal bytes without that projection.

No-response evidence

Still live on current main (verified 2026-11-03 via gh api raw reads)

pkgs/id/src/world_replication_net.rs on main: serve_stream calls ensure_nonce(&request.session_nonce)? (:109), then hub.lease(request.world.as_deref(), None) (:110-113), then replication_feed() (:114) — still no authorization check between the nonce check and opening the feed.

Acceptance criteria

  • Replication requires an authorized read credential/capability before the lease/feed is opened; unauthorized peers are rejected before any journal bytes are sent.
  • A regression test covers an unauthorized peer being refused (including the default-world case when world is omitted).
  • Or: a documented decision on this issue explaining why unauthenticated replication is acceptable, with the exposure stated explicitly.

Found by the pr-comment-review job from an unaddressed PR review comment.

Activity

  1. added
    kind/securitySecurity issue or security hardening
    task/triageQueue packet kind: verify, reproduce, and route this item
    priority/highImportant and time-sensitive
    queue/queuedHas a queued task in the Supabase agent queue
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    on Nov 3, 2026
  2. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

  3. added
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 3, 2026
  4. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  5. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · triage verdict: CONFIRMED

    • Agent: queue worker (repo-worker-code), lane code-k
    • Session / ID: task 6704 (triage)
    • Found via: queue claim

    Verdict: CONFIRMED live on current main (developing-today/code main @ 22fd4a37aeeb, re-read fresh via gh api raw contents this shift — an independent re-verification, not a copy of the packet):

    • pkgs/id/src/world_replication_net.rs serve_stream: :109 ensure_nonce(&request.session_nonce)? — and ensure_nonce (:181-186) rejects only an all-zero nonce. :110-113 hub.lease(request.world.as_deref(), None), :114 replication_feed(), after which the full snapshot and live tail stream to the peer.
    • ReplicationRequest (:37-43) carries exactly two fields — world: Option<String> and session_nonce — no credential, capability, or token field exists to check.
    • The protocol handler never reads the connecting peer's identity (no remote_id/peer-identity use anywhere in the handler path), so there is nothing to authorize against even if a check were added downstream.
    • WorldHub::lease (pkgs/id/src/world_hub.rs:503-517): the second parameter is an admin token that only gates creating a not-yet-existing world (create = admin.is_some_and(...)); for an existing world, naming it suffices — the hub's own test comment at :1001 states "Once it exists, anyone may name it". Passing None from the replication path therefore opens any existing world, and omitting world selects the default world.
    • Contrast stands: the records//id-world/1 path applies capability validation and a participant projection (chat hidden, seeds zeroed); this stream sends raw journal bytes with neither.

    Disposition: this is a genuine P1 — unauthenticated disclosure of journaled chat, inputs, and host events to any peer that can reach the endpoint and name (or omit) a world. No documented-decision rationale exists on this issue or PR #4589. A fix task implementing issue #4723's acceptance criteria (authorized read credential/capability checked before the lease/feed opens; regression test for an unauthorized peer, including the omitted-world default case) has been enqueued from this triage.

  6. 18 remaining items

  7. added
    queue/queuedHas a queued task in the Supabase agent queue
    and removed
    work/assignedQueue worker lifecycle state
    on Nov 3, 2026
  8. developing-today commented on Nov 3, 2026

    @developing-today
    Contributor

    agent-note:v1
    state: triaged (coverage pass)
    Coverage pass 2026-11-03: reviewed in the fleet-wide per-issue pass (drift / overlap / already-handled / stale / blockers / ordering). Verdict: still live; queued in repo-health-code-pilot as triage at priority 50 (priority/high). If this is already handled at current main, the triage closes it with evidence; if it needs splitting, the triage files the split.
    Found via: coverage pass 2026-11-03

  9. added
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    status/triagedTriaged and routed (see agent note)
    on Nov 4, 2026
  10. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    event: picked_up
    task: 7565 (repo-health-code-pilot; worker repo-worker-code; OpenChamber scheduled lane 335a5d1f-bfc1-4831-99ea-850c8939a727)
    state: claimed for current-state triage.
    verified: #4723 remains OPEN. Source PR #4589 is MERGED as-is. The fix path is already owned: PR #4744 (fix/4723-world-replication-read-capability) is OPEN and authored by Drew; prior queue triage records fix task 6712 as its owner. I will not touch or duplicate that PR/task.
    next: record the fresh triage verdict and keep #4723 open until the existing authorization fix merges.

  11. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    Triage verdict — confirmed, fix already in canonical PR

    Fresh check (2026-11-04): #4723 remains OPEN and the finding is still live on main. A fresh read of pkgs/id/src/world_replication_net.rs shows serve_stream runs ensure_nonce, then opens hub.lease(request.world.as_deref(), None), then replication_feed(); no authorization occurs before the journal feed opens. The source PR #4589 is MERGED as-is.

    Severity: P1 data disclosure. The raw replication stream exposes unprojected world-journal entries; the default-world case is reachable when world is omitted. The previously recorded fix decision remains appropriate: require the hub admin credential before resolving the lease/feed, rather than reusing participant capabilities that apply only to the projected records path.

    Existing fix path; no parallel work: PR #4744 (fix(id): authorize raw world replication feed) is OPEN, authored by Drew, and its description matches the acceptance criteria: bounded admin credential checked before hub.lease, omitted-world unauthorized-peer regression, and credential redaction. Queue fix task 6712 is already done with that PR as its outcome. This lane has not touched the PR or created duplicate work.

    Merge-validation gap to resolve on the owning PR: fresh gh pr checks shows only CodeRabbit passing. The PR body notes .github/workflows/id-check.yml lacks a pull_request trigger, so the id-crate PR checks did not run; local focused tests are recorded in the PR body but do not replace the missing CI coverage.

    Disposition: this triage is complete; leave #4723 OPEN until #4744 merges and required validation is satisfied. No new follow-up task filed.

  12. added
    status/triagedTriaged and routed (see agent note)
    and removed
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    on Nov 4, 2026
  13. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    event: triaged
    task: 7565 (repo-health-code-pilot; worker repo-worker-code; OpenChamber scheduled lane 335a5d1f-bfc1-4831-99ea-850c8939a727)
    state: triage complete; #4723 remains OPEN pending the existing fix PR.
    verified: current main still lacks authorization before the raw journal feed opens; canonical fix PR #4744 is OPEN and Drew-authored; its queue task 6712 is already done. Only CodeRabbit currently passes; id-check CI did not run.
    next: owning PR lane resolves required CI coverage, then merge #4744; no parallel task filed.

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

Metadata

Metadata

Assignees

Labels

app/idPackage/app: id (pkgs/ or apps/ in this repo)kind/securitySecurity issue or security hardeningpriority/highImportant and time-sensitivesource/pr-comment-reviewFiled by the pr-comment-review jobstatus/triagedTriaged and routed (see agent note)task/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions