Affected Versions
| Affected | not applicable (first-party code) |
| Fixed in | not applicable (first-party code) — see referenced commit |
| Ecosystem | N/A |
| CVE / GHSA | not assigned |
| CWE | CWE-862: Missing Authorization |
The Vulnerability Explained
The SchedulePush plugin's disableReminder() method trusted the user_id property from an event object without verifying that the requesting party was authorized to modify that specific user's push notification subscription state.
Here's the vulnerable code pattern:
async disableReminder(e) {
const userId = e.user_id;
await DataManager.setReminderStatus(userId, false);
await e.reply("✅ 已关闭课表订阅");
return true;
}
The critical flaw: e.user_id is accepted and passed directly to DataManager.setReminderStatus() with no validation. In bot framework configurations where event objects can be constructed or modified—whether through message spoofing, relay attacks, or certain plugin interaction patterns—an attacker could supply arbitrary user_id values.
Real-world impact: An attacker could systematically disable schedule reminders for other users, disrupting their notification workflows. In educational or scheduling bot deployments where timely reminders are critical, this could cause missed classes, appointments, or deadlines.
Attack scenario: A malicious user interacting with a bot instance crafts a message that reaches disableReminder with e.user_id set to a victim's identifier. The DataManager faithfully disables that victim's reminders, with no indication that the requester wasn't the legitimate user.
The Fix
The patch introduces authorization validation through the existing checkFriend() utility, coupled with getBotName() for proper error messaging:
Before:
async disableReminder(e) {
const userId = e.user_id;
await DataManager.setReminderStatus(userId, false);
await e.reply("✅ 已关闭课表订阅");
return true;
}
After:
async disableReminder(e) {
const userId = e.user_id;
const botName = getBotName(e);
if (!checkFriend(userId)) {
await e.reply(
`❌ 操作失败!请先添加${botName}为好友,才能管理课表订阅哦~\n`
);
return false;
}
await DataManager.setReminderStatus(userId, false);
await e.reply("✅ 已关闭课表订阅");
return true;
}
The fix works by leveraging the bot framework's existing friendship system. checkFriend(userId) verifies that the user has established a bidirectional friendship relationship with the bot instance—something that cannot be spoofed through simple event property manipulation. The getBotName(e) call ensures the error message correctly identifies the specific bot instance in multi-bot deployments.
Key Takeaways
-
Event object properties are not authentication:
e.user_idindicates which user the event claims to represent, not who sent the request or whether they're authorized to act on that user's behalf. -
Re-use existing authorization infrastructure: The fix didn't build new auth logic—it applied the existing
checkFriend()pattern that was already implemented elsewhere in the codebase, ensuring consistent security policy enforcement. -
Friendship verification matters for bot security: In bot frameworks, friendship establishment represents a trust boundary that should gate sensitive operations; skipping it for "convenience" creates authorization gaps.
-
State-modifying operations need explicit guards: Any method that changes persistent state (
setReminderStatus) should validate authorization at the entry point, not assume upstream validation occurred.
How Orbis AppSec Detected This
Source: The e.user_id property from the event object passed to disableReminder(e)
Sink: DataManager.setReminderStatus(userId, false) which modifies persistent subscription state
Missing control: No call to checkFriend() or equivalent authorization verification before the state-changing operation
CWE: CWE-862 — Missing Authorization
Fix: Added checkFriend(userId) validation with appropriate error handling using getBotName(e) for user feedback
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 exemplifies a common pattern in bot framework development: assuming that reaching a message handler implies proper authorization. The disableReminder() method's trust in e.user_id allowed arbitrary subscription manipulation until the checkFriend() gate was added. For developers building on similar frameworks, the lesson is clear—verify relationships, not just message routing.