Introduction
In the plugins/tools-delete.js file of a WhatsApp bot framework, we discovered a critical authorization bypass that allowed any group member to delete arbitrary messages. The handler defined commands like del, delete, and unsend, and while it correctly required the bot to have admin privileges (handler.botaadmin = true), it completely omitted any check verifying that the user invoking the command was also an admin.
This is a textbook example of Broken Access Control — the system verified it could perform an action, but never verified the requesting user should be allowed to trigger it. For developers building bot frameworks, chat plugins, or any command-handler architecture, this pattern is dangerously easy to overlook.
The Vulnerability Explained
The Vulnerable Code
Here's the original handler configuration in plugins/tools-delete.js:
let handler = async (m, { conn, command }) => {
// ... message deletion logic
};
handler.help = ['del', 'delete'];
handler.tags = ['tools'];
handler.botaadmin = true;
handler.command = ['del', 'delete', 'unsend'];
Notice the critical gap: handler.botaadmin = true tells the framework that the bot needs admin privileges in the group to execute this command. This is a capability check — it ensures the bot can technically delete messages. However, there is no handler.admin = true property, which would verify that the user sending the command has admin privileges.
How It Could Be Exploited
Consider this attack scenario:
- A WhatsApp group has the bot installed with admin privileges
- A regular (non-admin) group member sends the command
/deletewhile quoting any message in the group - The bot receives the command, checks that it has admin privileges (it does), and proceeds to delete the targeted message
- The non-admin user has effectively gained admin-level message deletion capabilities
This means any of the potentially hundreds of members in a group could:
- Delete important announcements from actual admins
- Remove evidence of harassment or rule violations
- Disrupt group communication by mass-deleting messages
- Undermine the authority structure of the group
Why This Is Critical
This vulnerability is particularly dangerous because:
- Low barrier to exploitation: Any group member can trigger it with a simple command — no technical skill required
- High impact: Message deletion is an irreversible, destructive action
- Library-level risk: This is a plugin in a bot framework, meaning every downstream deployment inherits this vulnerability
- False sense of security: The presence of
handler.botaadmin = truegives the impression that authorization is being enforced, when in reality it's only checking the bot's capabilities, not the user's permissions
The Fix
The fix is elegant in its simplicity — a single line addition that leverages the framework's built-in authorization system:
Before (Vulnerable)
handler.help = ['del', 'delete'];
handler.tags = ['tools'];
handler.botaadmin = true;
handler.command = ['del', 'delete', 'unsend'];
After (Fixed)
handler.help = ['del', 'delete'];
handler.tags = ['tools'];
handler.admin = true;
handler.botaadmin = true;
handler.command = ['del', 'delete', 'unsend'];
How This Solves the Problem
The addition of handler.admin = true on line 19 instructs the bot framework to verify that the user invoking the command has admin privileges in the group before the handler function executes. The framework's middleware intercepts the command, checks the sender's role in the group metadata, and only passes execution to the handler if the user is confirmed as a group admin.
This creates a proper dual-authorization model:
- handler.admin = true → The user must be a group admin (authorization check)
- handler.botaadmin = true → The bot must be a group admin (capability check)
Both conditions must be satisfied for the delete operation to proceed.
Prevention & Best Practices
1. Always Pair Capability Checks with Authorization Checks
When a handler requires elevated privileges to execute (like botaadmin), always ask: "Should every user be able to trigger this?" If the answer is no, add the corresponding user-level authorization check.
2. Apply the Principle of Least Privilege
Default to requiring the highest reasonable permission level for destructive operations. Message deletion, user kicks, and configuration changes should always require admin authorization.
3. Audit All Command Handlers Systematically
Review every handler in your bot plugin directory for the pattern:
// DANGEROUS: Has botaadmin but no admin check
handler.botaadmin = true;
// MISSING: handler.admin = true;
A simple grep can identify this:
grep -l "botaadmin" plugins/*.js | xargs grep -L "handler.admin"
4. Use Framework Middleware for Authorization
Rather than implementing authorization checks inside each handler function, use the framework's declarative properties (handler.admin, handler.owner, etc.) which are enforced consistently by middleware before handler execution.
5. Reference Standards
- OWASP Top 10 (2021): A01:2021 – Broken Access Control
- CWE-862: Missing Authorization
- OWASP Authorization Cheat Sheet: Implement role-based access control at every privileged operation
Key Takeaways
handler.botaadminis NOT an authorization check — it only verifies the bot's capabilities, not the user's permissions. Always pair it withhandler.admin = truefor privileged operations.- Destructive operations like message deletion require explicit user authorization — the
tools-delete.jshandler allowed any group member to delete messages because it confused capability with authorization. - One missing line can create a critical vulnerability — the entire fix was adding
handler.admin = true, demonstrating how small oversights in access control configuration have outsized security impact. - Bot frameworks are libraries — vulnerabilities in plugins affect every downstream deployment, amplifying the blast radius of a single missing check.
- Declarative authorization properties are only effective when used — the framework provided
handler.adminas a built-in mechanism, but it was simply never applied to this handler.
How Orbis AppSec Detected This
- Source: Any WhatsApp group member sending a
/del,/delete, or/unsendcommand - Sink: The message deletion logic in the
handlerasync function inplugins/tools-delete.js:1 - Missing control: No
handler.admin = trueproperty to enforce user-level admin authorization before executing the privileged delete operation - CWE: CWE-862 (Missing Authorization)
- Fix: Added
handler.admin = trueto the handler configuration to require admin privileges for the invoking user
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 in plugins/tools-delete.js is a clear illustration of how Broken Access Control manifests in real-world code. The distinction between "can the system perform this action?" and "should this user be allowed to request this action?" is fundamental to secure design, yet it's one of the most commonly overlooked checks in application development.
The fix — a single property addition — demonstrates that security improvements don't always require complex refactoring. Sometimes the most critical fixes are the simplest ones. For developers building bot frameworks, chat plugins, or any system with command handlers, the lesson is clear: every privileged operation needs an explicit authorization gate, regardless of what capability checks are already in place.