Affected Versions
| Affected | not applicable (first-party code) |
| Fixed in | not applicable (first-party code) — see linked PR |
| Ecosystem | N/A (Discord.js bot application) |
| CVE / GHSA | not assigned |
| CWE | CWE-269 (Improper Privilege Management) |
The Vulnerability Explained
Discord's permission system relies on role hierarchy: users can only manage roles positioned below their highest assigned role. A moderator with a "Moderator" role cannot assign or modify an "Administrator" role positioned above it, even if they hold the ManageRoles permission.
The auto-role management feature in this Discord.js bot broke this fundamental invariant. When processing subcommands like setup, enable, or list, the code validated that the bot could manage the target role—but never checked if the invoking user had sufficient hierarchy to do so.
The vulnerable validation looked like this:
// Bot permission check only—no user hierarchy validation
if (!interaction.guild.members.me.permissions.has(PermissionFlagsBits.ManageRoles)) {
return interaction.editReply({
content: lang?.role?.botNoPermission || "Bot không có quyền quản lý role.",
});
}
This pattern appears sufficient at first glance. The bot verifies it can manage roles, proceeds with the operation, and Discord's API will enforce its own restrictions. But Discord's API restrictions apply to the bot's identity, not the user's intent. The bot was correctly permissioned; the user was over-empowered.
The Attack Scenario
Consider a typical server hierarchy:
- Server Owner
- Administrator (full server access)
- Moderator (kick, ban, manage messages)
- Member
A user with the "Moderator" role has ManageRoles permission. In normal Discord usage, they cannot touch the "Administrator" role. But with this vulnerability:
/role setup role:@Administrator channel:#welcome
The command succeeds. The Administrator role is now automatically assigned to every new member joining the server. The moderator has effectively granted administrative access to the entire server population—without ever holding that role themselves.
The escalation is persistent and systemic: new members automatically receive elevated privileges, and the original attacker needs no ongoing access to maintain the compromise.
The Fix
The remediation adds explicit role hierarchy validation before any auto-role configuration:
// Kiểm tra người dùng có quyền quản lý role này không (tránh leo thang đặc quyền qua auto role)
if (role.position >= interaction.member.roles.highest.position && interaction.guild.ownerId !== interaction.user.id) {
return interaction.editReply({
content:
lang?.role?.userRoleTooLow || "Bạn không thể quản lý role này vì role của bạn thấp hơn hoặc bằng role này.",
});
}
This single check enforces Discord's native hierarchy semantics:
| Before | After |
|---|---|
| Bot checks its own ManageRoles permission | Bot checks its permission + user's role hierarchy |
| Any ManageRoles holder configures any auto-role | User must hold a role strictly higher than target |
| Server owner subject to same checks | Server owner explicitly exempted |
The >= comparison is critical. Discord's role system uses strictly higher position for management rights—a user with position 5 cannot manage position 5 or above. The exemption for interaction.guild.ownerId preserves Discord's guarantee that server owners retain full control regardless of role assignments.
Key Takeaways
-
Permission bits are insufficient for hierarchy-sensitive operations:
ManageRolesinPermissionsBitFielddoesn't encode positional information. Always validaterole.positionagainstGuildMember.roles.highest.positionfor role management commands. -
Bot-centric validation creates user-level vulnerabilities: When a bot acts on behalf of users, it must enforce the user's restrictions, not merely its own. The bot's permissions are a prerequisite, not a substitute for authorization checks.
-
Auto-assignment features amplify privilege escalation impact: A single compromised auto-role configuration affects all future server members. The persistence makes this vulnerability more severe than transient role grants.
-
Discord.js interaction objects expose hierarchy data:
interaction.member.roles.highestprovides the necessary comparison point—no additional API calls required.
How Orbis AppSec Detected This
Source: The role option in Discord slash command interactions, accepting user-specified role IDs.
Sink: Database persistence via database.ZiGuild.findOne() and subsequent ZiGuild document updates that configure automatic role assignment.
Missing control: No validation of interaction.member.roles.highest.position against the target role.position before persisting auto-role configuration.
CWE: CWE-269 (Improper Privilege Management)
Fix: Added hierarchy check using role.position >= interaction.member.roles.highest.position with explicit server owner exemption.
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
This vulnerability demonstrates how bot-mediated role management can subvert Discord's built-in hierarchy protections. The fix restores the platform's security model by enforcing that users can only configure auto-roles they could manually assign. For Discord.js developers, the pattern is clear: always validate roles.highest.position before acting on user-specified roles, regardless of what permission bits appear sufficient.