CVE-2026-111937 Overview
CVE-2026-111937 is a missing authorization vulnerability [CWE-862] affecting Frappe CRM versions prior to 1.81.2. The flaw stems from absent permission hooks on the CRM Notification doctype, allowing authenticated users with the Sales User role to access notification records belonging to other users. Attackers can query frappe.client endpoints with a to_user filter or invoke mark_as_read with another user argument. This enables reading, marking as read, or deleting notifications owned by any other user in the system.
Critical Impact
Authenticated Sales Users can read, modify, or delete other users' CRM notifications, exposing sales activity metadata and enabling tampering with notification state.
Affected Products
- Frappe CRM versions prior to 1.81.2
- crm/api/notifications.py endpoints exposed via Frappe framework
- CRM Notification doctype without permission hooks
Discovery Timeline
- 2026-10-19 - CVE-2026-111937 published to NVD
- 2026-10-19 - Last updated in NVD database
Technical Details for CVE-2026-111937
Vulnerability Analysis
The vulnerability resides in Frappe CRM's handling of the CRM Notification doctype. The crm_notification.json definition and hooks.py configuration omitted the get_permission_query_conditions and has_permission hooks required by the Frappe framework to scope records to their owner. Without these hooks, any authenticated user with permissions to read the doctype receives unfiltered access across all notification rows.
Two exposed primitives amplify the exposure. First, the generic frappe.client.get_list and frappe.client.get endpoints accept arbitrary filters, letting an attacker filter on to_user to enumerate another user's notifications. Second, the mark_as_read function in crm/api/notifications.py accepted a user parameter defaulting to the caller's session, enabling callers to specify any target user and modify their notification state.
Root Cause
The root cause is a missing authorization check [CWE-862] on a doctype that stores per-user records. Frappe doctypes require explicit get_permission_query_conditions and has_permission functions to enforce row-level isolation. These functions were not registered for CRM Notification, and the mark_as_read API trusted a client-supplied user argument without verifying it matched the session user.
Attack Vector
An authenticated user with the Sales User role sends a request to frappe.client.get_list targeting the CRM Notification doctype with a to_user filter set to another user's identifier. The server returns matching rows. The attacker can then call mark_as_read or deletion endpoints referencing the same user to tamper with records.
# Security patch in crm/api/notifications.py
# fix: scope CRM notifications to their recipient
@frappe.whitelist()
-def mark_as_read(user: str | None = None, doc: str | None = None):
- user = user or frappe.session.user
+def mark_as_read(doc: str | None = None):
+ user = frappe.session.user
filters = {"to_user": user, "read": False}
or_filters = []
if doc:
Source: GitHub Commit a24d73a
The companion patch registers permission hooks on the doctype module:
# crm/fcrm/doctype/crm_notification/crm_notification.py
def get_permission_query_conditions(user=None):
if not user:
user = frappe.session.user
if user == "Administrator" or "System Manager" in frappe.get_roles(user):
return ""
return f"`tabCRM Notification`.`to_user` = {frappe.db.escape(user)}"
def has_permission(doc, ptype, user):
if not user:
user = frappe.session.user
if user == "Administrator" or "System Manager" in frappe.get_roles(user):
return True
if ptype == "create" or not doc.to_user:
return True
return doc.to_user == user
Source: GitHub Commit a24d73a
Detect Identity Attacks the Moment Authentication Fails
Singularity™ Identity surfaces credential misuse, lateral movement, and privilege escalation in real time, so you can contain the breach before it spreads.
Stop Identity AttacksDetection Methods for CVE-2026-111937
Indicators of Compromise
- Requests to /api/method/frappe.client.get_list with doctype=CRM Notification and a to_user filter not matching the session user.
- Calls to /api/method/crm.api.notifications.mark_as_read where the user parameter differs from the authenticated session user.
- Unexpected deletions of CRM Notification records across multiple user accounts in a short interval.
Detection Strategies
- Parse Frappe application logs for frappe.client queries targeting CRM Notification and compare the filters.to_user value against the request's session user.
- Alert on legacy mark_as_read invocations that still include a user parameter after upgrade, since the patched signature no longer accepts it.
- Baseline per-user notification read and delete rates, then flag outlier accounts touching records across many recipients.
Monitoring Recommendations
- Enable verbose audit logging on the Frappe REST API gateway and forward events to a centralized analytics backend for correlation.
- Monitor role assignments for the Sales User role and review any accounts recently granted that role in untrusted contexts.
- Track version strings reported by the Frappe CRM instance to confirm 1.81.2 or later is deployed across all environments.
How to Mitigate CVE-2026-111937
Immediate Actions Required
- Upgrade Frappe CRM to version 1.81.2 or later as published in the GitHub Release v1.81.2.
- Review audit logs for frappe.client.get_list and mark_as_read activity targeting other users and revoke sessions associated with suspicious accounts.
- Rotate credentials for Sales User accounts that may have accessed unauthorized notification data.
Patch Information
The fix is published in Frappe CRM 1.81.2 via commit a24d73a. It adds get_permission_query_conditions and has_permission functions scoping CRM Notification records to their recipient and removes the client-controllable user parameter from mark_as_read. See the GitHub Security Advisory GHSA-4rjw-6mxx-m345 and the VulnCheck Advisory on Frappe CRM for additional context.
Workarounds
- Restrict access to the Frappe REST API from untrusted networks using a reverse proxy allowlist until the patch is applied.
- Temporarily remove read and write permissions on CRM Notification for the Sales User role if upgrade cannot be performed immediately.
- Add a server-side filter proxy that rejects frappe.client requests on CRM Notification when the to_user filter does not match the session user.
# Verify the deployed Frappe CRM version meets the patched release
bench --site <site-name> list-apps | grep crm
# Expected output should show crm version 1.81.2 or later
bench --site <site-name> update --apps crm
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

