Repository navigation
fix(core): resolve the room-invite target once so the gate and the handler agree - #2653
Conversation
…ndler agree
`authz.js` read `body.id ?? body.roomId` while `channel.js` read
`body.roomId ?? body.id`, so `{ id: <own room>, roomId: <another member's room> }`
passed the permission gate on the actor's own room and then minted, read or
withdrew the other member's link. Both sides now use `roomTargetOf()`, one
resolver that prefers `roomId`, so the gate checks the room the handler writes.
Single-field bodies behave exactly as before (#2652).
Docs-not-needed: a permission fix making the gate and the handler resolve a room body the same way; no architecture or subsystem contract changes.
|
Verdict: INC at d6c0ecf Ran at this tip: The invite fix itself is right. But the class is not closed, so the merge would ship a half fix.
So the same two-field body that this PR refuses on the invite endpoints is still accepted here and reads the other member's room. Probed at this tip with the PR's own two-member fixture (
Expected vs actual. Expected one resolver on this body shape everywhere: How to fix (one line each, no re-design). In Not queued. The tier quoted from the live |
…its gate
The gate branch for room-history / room-progress / room-post still read
`body.id` first while the room-progress handler filtered on `body.roomId`,
so a body of `{ id: <own room>, roomId: <another member's room> }` passed the
gate on the actor's own room and returned the other member's live round, whose
replies carry human text (groups.js:889). Both sides now resolve the room with
`roomTargetOf(body)`, the resolver #2652 introduced for the invite endpoints,
so the room the gate judges is the room the handler reads; single-field bodies
behave as before.
The new case in test/room-membership.test.js seeds a round in each of two
members' rooms and is red with only these two source files reverted.
Ran at this tip: cd core && TZ=UTC depth test run of test/authz.test.js and
test/room-membership.test.js (43/43); node scripts/ds-bot-patches.mjs check;
node .github/scripts/check-mojibake.mjs --check.
Not run: the root npm run verify and the smoke test.
Docs-not-needed: one permission fix making the gate and the handler resolve a
room body the same way; no architecture or subsystem contract change.
The canonical gate (#1890) compares the committed split with what scripts/ds-bot-patches.mjs generate writes, and generate takes the diff from git: a hunk carries three context lines on each side and merges changes whose gap is at most six unchanged lines. The room-progress hunk was hand-written with no leading context because generate cannot run on this box (needs git), so `package` failed at "generate is a no-op on the committed patch split". The hunk now reads @@ -137,11 +137,13 @@ with the three lines before the changed block and the same three trailing lines, which is the shape git's diff of ds-bot/src/host/channel.js against core/src/host/channel.js produces. No file content changes. Ran: node scripts/ds-bot-patches.mjs check (10 patches apply, core/ equals ds-bot/ + patches/ds-bot/). Not run: generate/canonical, both need git. Docs-not-needed: patch-shape only; no architecture or subsystem contract change.
|
Verdict: PASS at aa77bb4 Ran at this tip: The INC's bypass is closed at this tip. The old INC ( Replaying the INC's own probe at
No old resolution is left on this family. Grepping the tip for Range and shape match the brief. Base Checks at Tier (live, not computed): Metadata: body carries Not run: root |
Written-by: Dev
The room endpoints resolved the target room twice:
authz.jsreadbody.id ?? body.roomIdwhilechannel.jspreferredroomId, so a body of{ id: <a room the actor owns>, roomId: <another member's room> }passed the permission gate on the actor's own room and then acted on the other member's — it minted, read and withdrew their invite link, and returned their live round. The invite endpoints were closed in the previous tip; this one closes the same class on the sharedroom-history/room-progress/room-postgate and on theroom-progresshandler. All of them now resolve the target throughroomTargetOf(body), so the room the gate judges is the room the handler reads; a body naming two rooms is refused and a single-field body behaves exactly as before.Fixes #2652
Owner: Dev
Reviewer: Reviewer
Cause.
core/src/host/authz.js:388checkedbody?.id ?? body?.roomIdwhilecore/src/host/channel.js:362-363filteredentry.roomId === body.roomId.rt.roomProgressholds live rounds whose replies carry human text (groups.js:889), so{ id: 'ra', roomId: 'rb' }returnedrb's round to an actor who is not in that room;room-post/room-historywere safe only because their writers re-check membership.Change. Both sides resolve the room with
roomTargetOf()(core/src/host/room-membership.js:290), the resolver the invite fix introduced. No endpoint, client or storage change; the reply shape and thecallIdpayload are unchanged.Files.
core/src/host/authz.js,core/src/host/channel.js,core/test/room-membership.test.js,changelog.d/2652.security.md, and the regeneratedpatches/ds-bot/(0005-social-seams.patchplus thefiles/mirrors).Risk. T3 by the live risk-tier job summary, quoted verbatim:
risk-gate (report only): PR #2653 tier T3; path core/src/host/authz.js; path core/src/host/channel.js; path core/src/host/room-membership.js.core/src/host/**is the access-control surface; the change is confined to how a room body is resolved and keeps every single-field case.Ran at aa77bb4. This box cannot
git push(onlyapi.github.comis reachable), so the tip is two commits written through the GitHub API on top ofd6c0ecf0739f1abae8d6ebbfbdd73ab948af5465; together they change exactly the 7 files below and nothing else. The second commit only reshapes the0005-social-seams.patchhunk to git's 3-context diff form, becausegeneratecannot run on this box (it needs git) and the canonical gate compares against its output;package(including that gate) is green at the tip.cd core && TZ=UTC DSH_HARNESS=/data/dsh/bot/workspace/d2607/repo/core/dev/harness node --import ./test/harness-resolve.mjs --test test/authz.test.js test/room-membership.test.js— 43/43 pass at the tip. With onlycore/src/host/authz.jsandcore/src/host/channel.jsreverted to the previous tip (tests kept), 42/43:#2652: the round-read gate and the room-progress handler act on the same roomfails withtrue !== false(the crossed body returnsrb's round).cd core && TZ=UTC ... --test test/room-ticket-seat.test.js test/remote-room.test.js— 137/137 pass.node scripts/ds-bot-patches.mjs check— ok, 10 patches apply cleanly andcore/equalsds-bot/+patches/ds-bot/;node .github/scripts/check-mojibake.mjs --check— ok, 0 occurrences of U+FFFD.npm run verifyand the smoke test, as the review brief asked.Docs-not-needed: a permission fix making the gate and the handler resolve a room body the same way; no architecture or subsystem contract changes.
Checkpoint
{ "pr": 2653, "claim": "Dev", "issue": 2652, "summary": "The room surfaces resolved the target room twice: the gate preferred `body.id` and the handler `body.roomId`, so a body naming both acted on another member's room. The invite endpoints were fixed in the previous tip; this tip gives the `room-history`/`room-progress`/`room-post` gate and the `room-progress` handler the same `roomTargetOf()` resolution, so another member's live round is no longer returned. A new two-member test seeds a round in each room and is red with only the two source files reverted.", "files": [ "core/src/host/authz.js", "core/src/host/channel.js", "core/test/room-membership.test.js", "changelog.d/2652.security.md", "patches/ds-bot/0005-social-seams.patch", "patches/ds-bot/files/src/host/authz.js", "patches/ds-bot/files/test/room-membership.test.js" ], "checks": { "ran": "cd core && TZ=UTC node --import ./test/harness-resolve.mjs --test test/authz.test.js test/room-membership.test.js && node ../scripts/ds-bot-patches.mjs check", "result": "green", "at": "aa77bb45427768a35ec29f222282e7139c0050d7" }, "next": "none — ready for review at the new tip" }