Skip to content

fix(discord): DISCORD_ALLOW_BOTS=mentions/all works without DISCORD_ALLOWED_USERS#11606

Merged
teknium1 merged 3 commits into
mainfrom
hermes/hermes-5b03d077
Apr 17, 2026
Merged

fix(discord): DISCORD_ALLOW_BOTS=mentions/all works without DISCORD_ALLOWED_USERS#11606
teknium1 merged 3 commits into
mainfrom
hermes/hermes-5b03d077

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Salvage of PR #4477 by @gnanam1990 with @gnanam1990 authorship preserved on the core fix commit.

Fixes #4466 (@alanjds).

Problem

DISCORD_ALLOW_BOTS=mentions and =all were completely ineffective. Two sequential authorization gates both rejected bot messages:

  • Gate 1on_message in gateway/platforms/discord.py ran _is_allowed_user BEFORE the bot filter.
  • Gate 2_is_user_authorized in gateway/run.py rejected bot IDs at the gateway level with Unauthorized user: <bot_id> even if a bot somehow made it past Gate 1.

Triggered by the #4466 reporter when a Cloudflare Worker webhook posting Notion events to Discord was silently ignored despite DISCORD_ALLOW_BOTS=mentions being set.

Fix

  • gateway/platforms/discord.py — reorder on_message so the bot filter runs BEFORE the user allowlist. Bots permitted by DISCORD_ALLOW_BOTS skip the human allowlist; non-bots are still checked.
  • gateway/session.py — add is_bot: bool = False to SessionSource.
  • gateway/platforms/base.py — expose is_bot in build_source.
  • gateway/platforms/discord.py _handle_message — set is_bot=True for bot authors.
  • gateway/run.py _is_user_authorized — when source.is_bot is True AND DISCORD_ALLOW_BOTS is mentions/all, return True early. Platform already validated; don't re-reject.

Behavior matrix

Config Before After
DISCORD_ALLOW_BOTS=none (default) Blocked ✓ Blocked ✓
DISCORD_ALLOW_BOTS=all Blocked ✗ Allowed ✓
DISCORD_ALLOW_BOTS=mentions + @mention Blocked ✗ Allowed ✓
DISCORD_ALLOW_BOTS=mentions, no mention Blocked ✓ Blocked ✓
Human in DISCORD_ALLOWED_USERS Allowed ✓ Allowed ✓
Human NOT in allowlist Blocked ✓ Blocked ✓

What we dropped from the original PR

PR #4477 bundled an unrelated agent/redact.py change fixing issue #4367 (lowercase Python variable false-positive redaction). Correct fix, but out of scope for this PR. Issue #4367 remains open for separate triage — the redact.py patch from @gnanam1990 is still a candidate for salvage there.

Test plan

  • New tests/gateway/test_discord_bot_auth_bypass.py — 6 regression cases covering the full behavior matrix + platform isolation (bypass doesn't leak to Telegram etc.): all pass.
  • pytest tests/gateway/ -k discord → 229 passed (no change from baseline).
  • pytest tests/gateway/ -k 'auth or allow or unauthor' → 134 passed, 1 skipped.

Commits

  1. e386ace7fix(discord): DISCORD_ALLOW_BOTS=... without DISCORD_ALLOWED_USERS (@gnanam1990, authorship preserved; manually applied since cherry-pick conflicted with the newer multi-agent filtering code on main)
  2. b5db3735test(discord): regression guard for DISCORD_ALLOW_BOTS auth bypass (ours — 6 new test cases)
  3. af25ea11chore(release): map @gnanam1990 to AUTHOR_MAP

gnanam1990 and others added 3 commits April 17, 2026 05:41
…RD_ALLOWED_USERS

Fixes #4466.

Root cause: two sequential authorization gates both independently rejected
bot messages, making DISCORD_ALLOW_BOTS completely ineffective.

Gate 1 — `discord.py` `on_message`:
    _is_allowed_user ran BEFORE the bot filter, so bot senders were dropped
    before the DISCORD_ALLOW_BOTS policy was ever evaluated.

Gate 2 — `gateway/run.py` _is_user_authorized:
    The gateway-level allowlist check rejected bot IDs with 'Unauthorized
    user: <bot_id>' even if they passed Gate 1.

Fix:

  gateway/platforms/discord.py — reorder on_message so DISCORD_ALLOW_BOTS
  runs BEFORE _is_allowed_user. Bots permitted by the filter skip the
  user allowlist; non-bots are still checked.

  gateway/session.py — add is_bot: bool = False to SessionSource so the
  gateway layer can distinguish bot senders.

  gateway/platforms/base.py — expose is_bot parameter in build_source.

  gateway/platforms/discord.py _handle_message — set is_bot=True when
  building the SessionSource for bot authors.

  gateway/run.py _is_user_authorized — when source.is_bot is True AND
  DISCORD_ALLOW_BOTS is 'mentions' or 'all', return True early. Platform
  filter already validated the message at on_message; don't re-reject.

Behavior matrix:

  | Config                                     | Before  | After   |
  | DISCORD_ALLOW_BOTS=none (default)          | Blocked | Blocked |
  | DISCORD_ALLOW_BOTS=all                     | Blocked | Allowed |
  | DISCORD_ALLOW_BOTS=mentions + @mention     | Blocked | Allowed |
  | DISCORD_ALLOW_BOTS=mentions, no mention    | Blocked | Blocked |
  | Human in DISCORD_ALLOWED_USERS             | Allowed | Allowed |
  | Human NOT in DISCORD_ALLOWED_USERS         | Blocked | Blocked |

Co-authored-by: Hermes Maintainer <hermes@nousresearch.com>
Six test cases covering:
- DISCORD_ALLOW_BOTS=mentions + bot not in DISCORD_ALLOWED_USERS → authorized
- DISCORD_ALLOW_BOTS=all + bot not in DISCORD_ALLOWED_USERS → authorized
- DISCORD_ALLOW_BOTS=none → bots still rejected (preserves security)
- DISCORD_ALLOW_BOTS unset → same as 'none'
- Humans still checked against allowlist even with allow_bots=all
- Bot bypass is Discord-specific — doesn't leak to other platforms

Guards against a regression where the is_bot bypass in _is_user_authorized
gets moved, removed, or accidentally extended to other platforms.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DISCORD_ALLOW_BOTS has no effect without also adding the bot to DISCORD_ALLOWED_USERS

2 participants