hermes - 💡(How to fix) Fix [SECURITY] DISCORD_ALLOWED_ROLES cross-guild DM bypass (CVSS 8.1) [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#12136Fetched 2026-04-19 15:25:34
View on GitHub
Comments
0
Participants
1
Timeline
0
Reactions
0
Author
Participants

Fix Action

Fix

PR #12135 scopes role checks to the originating guild and disables role-based DM auth by default, with an explicit opt-in (DISCORD_DM_ROLE_AUTH_GUILD=<guild_id>) for operators who want it for a single trusted guild.

  • 9 regression tests covering the bypass, the opt-in, the cross-guild guild-message bypass, and backwards-compat user-ID paths.
  • 47/47 discord-auth tests pass. Zero regressions.

Code Example

# Fallback: scan mutual guilds for member's roles
if self._client is not None:
    ...
    for guild in self._client.guilds:   # <-- cross-guild scan
        m = guild.get_member(uid_int)
        if m is None:
            continue
        m_roles = getattr(m, "roles", None) or []
        if any(getattr(r, "id", None) in allowed_roles for r in m_roles):
            return True
RAW_BUFFERClick to expand / collapse

Security issue

Severity: CVSS 8.1 (High) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N

The DISCORD_ALLOWED_ROLES allowlist introduced in #11608 (authored by @0xyg3n in #9873) is not guild-scoped. Role membership in any mutual guild authorizes the user globally, including in DMs where no originating guild context exists.

Exploit path

  1. Operator runs Hermes with DISCORD_ALLOWED_ROLES=<role_id> intending to authorize moderators of a private trusted server B.
  2. The bot is also in a large public server A (community server, support server, etc.).
  3. An attacker obtains <role_id> in server A (role-ID collisions across servers are easy to engineer; many public servers hand out colorful non-privileged roles on reaction or join).
  4. Attacker DMs the bot. The allowlist iterates self._client.guilds, finds the role in server A, and authorizes the DM.
  5. Attacker now has authenticated access to the bot: tool calls, memory reads, LLM calls billed to the operator, file access, any cross-service side effect the bot exposes.

The same flaw permits authorized chat in guild A's channels when the role was configured for guild B, by iterating mutual guilds instead of checking message.guild.

Why this is critical

  • No operator is safe. Any Hermes deployment with DISCORD_ALLOWED_ROLES configured is exposed if the bot joins any public server.
  • Scope-changed (S:C): privilege obtained in one guild applies to a fundamentally different guild/DM — classic authorization boundary break.
  • No mitigation short of disabling DISCORD_ALLOWED_ROLES until fixed. Operators who don't know about the flaw have no signal.

Code location

gateway/platforms/discord.py, function _is_allowed_user:

# Fallback: scan mutual guilds for member's roles
if self._client is not None:
    ...
    for guild in self._client.guilds:   # <-- cross-guild scan
        m = guild.get_member(uid_int)
        if m is None:
            continue
        m_roles = getattr(m, "roles", None) or []
        if any(getattr(r, "id", None) in allowed_roles for r in m_roles):
            return True

Two flaws:

  1. Signature takes no guild / message argument, so callers can't provide origin context.
  2. DM path (guild=None) silently falls back to the cross-guild scan.

Fix

PR #12135 scopes role checks to the originating guild and disables role-based DM auth by default, with an explicit opt-in (DISCORD_DM_ROLE_AUTH_GUILD=<guild_id>) for operators who want it for a single trusted guild.

  • 9 regression tests covering the bypass, the opt-in, the cross-guild guild-message bypass, and backwards-compat user-ID paths.
  • 47/47 discord-auth tests pass. Zero regressions.

Recommendation

  1. Merge #12135 as a security fix.
  2. Consider a GHSA advisory once merged — anyone with DISCORD_ALLOWED_ROLES set in production is currently exposed.
  3. Release notes should flag the DISCORD_ALLOWED_ROLES behavior tightening and the new DISCORD_DM_ROLE_AUTH_GUILD opt-in.

References

  • #7871 — original feature request (role-based allowlist)
  • #11608 — initial implementation (merged on main)
  • #9873 — PR that implemented the feature (same author as this report)
  • #12135 — fix PR (this issue)

cc @teknium1

extent analysis

TL;DR

The most likely fix for the security issue is to merge PR #12135, which scopes role checks to the originating guild and disables role-based DM auth by default.

Guidance

  • Review the code changes in PR #12135 to understand the fixes for the two identified flaws: the lack of guild scoping and the silent fallback to cross-guild scan in DM paths.
  • Verify that the regression tests (9 tests) and discord-auth tests (47 tests) pass to ensure no regressions are introduced.
  • Consider the security implications of the current implementation and the benefits of the proposed fix, including the introduction of an explicit opt-in for role-based DM auth.
  • Evaluate the need for a GHSA advisory to inform users of the potential exposure if DISCORD_ALLOWED_ROLES is set in production.

Example

No code snippet is provided as the issue already includes the relevant code location and the proposed fix in PR #12135.

Notes

The fix is specific to the DISCORD_ALLOWED_ROLES feature and its interaction with guilds and DMs. The proposed solution introduces a new opt-in mechanism for role-based DM auth, which may require updates to operator configurations.

Recommendation

Merge PR #12135 as a security fix to address the identified vulnerabilities and prevent potential exploits. This fix provides a more secure implementation of role-based allowlists and introduces an explicit opt-in for role-based DM auth.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING