diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.py b/frappe/email/doctype/auto_email_report/auto_email_report.py index faf81f3e65..1b79794a64 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.py +++ b/frappe/email/doctype/auto_email_report/auto_email_report.py @@ -392,7 +392,7 @@ def make_links(columns, data): def update_field_types(columns): for col in columns: if col.fieldtype in ("Link", "Dynamic Link", "Currency") and col.options != "Currency": - col.fieldtype = "Data" + col.fieldtype = "HTML Editor" col.options = "" return columns diff --git a/frappe/tests/test_formatter.py b/frappe/tests/test_formatter.py index 565c321725..aa3db6b2b0 100644 --- a/frappe/tests/test_formatter.py +++ b/frappe/tests/test_formatter.py @@ -1,6 +1,7 @@ import frappe from frappe import format from frappe.tests import IntegrationTestCase +from frappe.utils.formatters import format_value class TestFormatter(IntegrationTestCase): @@ -17,3 +18,37 @@ class TestFormatter(IntegrationTestCase): self.assertEqual(format(100000, df, doc, format="#,###.##"), "$ 100,000.00") frappe.db.set_default("currency", None) + + def test_safe_formatting(self): + """Test that in certain field types, the values are escaped.""" + payload = "" + sanitized_payload = "<script>alert('testing')</script>" + + data_df = frappe._dict({"fieldname": "book_name", "fieldtype": "Data"}) + self.assertEqual(format_value(payload, data_df), sanitized_payload) + + text_df = frappe._dict({"fieldname": "book_description", "fieldtype": "Text"}) + self.assertEqual(format_value(payload, text_df), sanitized_payload) + + html_df = frappe._dict({"fieldname": "book_title", "fieldtype": "HTML Editor"}) + self.assertEqual(format_value(payload, html_df), payload) + + editor_df = frappe._dict({"fieldtype": "Text Editor"}) + formatted_editor = format_value("Bold", editor_df) + self.assertEqual(formatted_editor, "
Bold
") + + ltext_df = frappe._dict({"fieldname": "book_long_description", "fieldtype": "Long Text"}) + self.assertEqual(format_value(payload, ltext_df), sanitized_payload) + + select_df = frappe._dict({"fieldtype": "Select", "parent": "Task"}) + value = "Open" + self.assertEqual(format_value(value, select_df), "Open") + self.assertEqual(format_value(payload, select_df), sanitized_payload) + + link_df = frappe._dict({"fieldtype": "Link", "options": "User"}) + self.assertEqual(format_value(payload, link_df, doc=None), sanitized_payload) + doc = frappe._dict({"__link_titles": {"User::attacker@example.com": ""}}) + formatted = format_value("attacker@example.com", link_df, doc) + self.assertIn("<svg", formatted) + + self.assertEqual(format_value(payload, df=None), sanitized_payload) diff --git a/frappe/utils/formatters.py b/frappe/utils/formatters.py index f04edb746f..7a2a5a46d9 100644 --- a/frappe/utils/formatters.py +++ b/frappe/utils/formatters.py @@ -60,6 +60,8 @@ def format_value(value, df=None, doc=None, currency=None, translated=False, form value = frappe._(value) if not df: + if isinstance(value, str): + return frappe.utils.escape_html(value) return value elif df.get("fieldtype") == "Date": @@ -99,7 +101,8 @@ def format_value(value, df=None, doc=None, currency=None, translated=False, form elif df.get("fieldtype") in ("Text", "Small Text"): if not BLOCK_TAGS_PATTERN.search(value): - return frappe.safe_decode(value).replace("\n", "
") + escaped_value = frappe.utils.escape_html(frappe.safe_decode(value)) + return escaped_value.replace("\n", "
") elif df.get("fieldtype") == "Markdown Editor": return frappe.utils.markdown(value) @@ -124,21 +127,28 @@ def format_value(value, df=None, doc=None, currency=None, translated=False, form elif df.get("fieldtype") in ["Link", "Dynamic Link"]: if not doc or not doc.get("__link_titles") or not df.options: - return value + return frappe.utils.escape_html(cstr(value)) doctype = df.options if df.get("fieldtype") == "Dynamic Link": if not df.parent: - return value + return frappe.utils.escape_html(cstr(value)) meta = frappe.get_meta(df.parent) _field = meta.get_field(df.options) doctype = _field.options - - return doc.__link_titles.get(f"{doctype}::{value}", value) + link_title = doc.__link_titles.get(f"{doctype}::{value}", value) + return frappe.utils.escape_html(cstr(link_title)) elif df.get("fieldtype") == "Select": if isinstance(value, str): - return frappe._(value, context=df.parent or "") + translated_value = frappe._(value, context=df.parent or "") + return frappe.utils.escape_html(translated_value) + + elif df.get("fieldtype") == "HTML Editor": + return value + + if isinstance(value, str): + value = frappe.utils.escape_html(value) return value