/guild posts staff-gated guild data into a public channel when a staff member runs it #46

Open
opened 2026-09-25 17:59:31 +00:00 by wtclaude · 0 comments
Member

Found while planning module-rust phase 16 (Discord slash commands), 2026-09-25. Not fixed there. It is out of that phase's scope.

The leak

server/commands/guild.command.js checks the caller's rung against the shard's guilds audience. That is the right check for whether to answer, but the permitted answer is then returned without ephemeral (the list form around line 163, and detail()), so the bot posts it into the channel. A slash-command reply is seen by everyone in the channel, not just the caller.

Failure scenario: an admin gates guilds to staff. A moderator runs /guild in a public channel. The guild list (or one guild's members, online count, linked-account count and leaders) is posted where every member of that Discord server can read it. The shard's setting said only staff may see that.

Refusals are already private: they set ephemeral: true, and the bot turns a publicly-deferred reply into an ephemeral follow-up (TEAMS.md §7.1, the Teams phase 7 fix). This is the reverse case: a permitted answer that is narrower than public.

The rule module-rust adopted (D127)

An answer that carries anything whose audience is narrower than public is returned ephemeral: true, privately to the caller. Answers the public may see stay public. For /guild, that means ephemeral: feature.audience !== 'public' on both the list and the detail envelopes.

Plan of record for the Rust side: docs/modules/rust/PLAN.md §32 (in progress).

  • AI-assisted: Claude Code (Claude Opus 5.5)
Found while planning module-rust phase 16 (Discord slash commands), 2026-09-25. Not fixed there. It is out of that phase's scope. ## The leak `server/commands/guild.command.js` checks the **caller's** rung against the shard's `guilds` audience. That is the right check for *whether* to answer, but the permitted answer is then returned **without `ephemeral`** (the list form around line 163, and `detail()`), so the bot posts it into the channel. A slash-command reply is seen by everyone in the channel, not just the caller. **Failure scenario:** an admin gates `guilds` to `staff`. A moderator runs `/guild` in a public channel. The guild list (or one guild's members, online count, linked-account count and leaders) is posted where every member of that Discord server can read it. The shard's setting said only staff may see that. Refusals are already private: they set `ephemeral: true`, and the bot turns a publicly-deferred reply into an ephemeral follow-up (TEAMS.md §7.1, the Teams phase 7 fix). This is the **reverse case**: a *permitted* answer that is narrower than public. ## The rule module-rust adopted (D127) An answer that carries anything whose audience is narrower than `public` is returned `ephemeral: true`, privately to the caller. Answers the public may see stay public. For `/guild`, that means `ephemeral: feature.audience !== 'public'` on both the list and the detail envelopes. Plan of record for the Rust side: `docs/modules/rust/PLAN.md` §32 (in progress). - [x] AI-assisted: Claude Code (Claude Opus 5.5)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RunicGateway/Module-uo#46
No description provided.