From 224426e06b029bcef7186ff54013ef71dd260737 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2026 00:46:54 +0530 Subject: [PATCH] fix(pos): escape html output in pos page templates (backport #55527) (#55529) Co-authored-by: Diptanil Saha Co-authored-by: Claude Sonnet 4.6 fix(pos): escape html output in pos page templates (#55527) --- .../page/point_of_sale/pos_controller.js | 2 +- .../page/point_of_sale/pos_item_cart.js | 63 +++++++++++-------- .../page/point_of_sale/pos_item_details.js | 12 ++-- .../page/point_of_sale/pos_item_selector.js | 8 ++- .../page/point_of_sale/pos_past_order_list.js | 8 +-- .../point_of_sale/pos_past_order_summary.js | 22 ++++--- .../selling/page/point_of_sale/pos_payment.js | 4 +- 7 files changed, 71 insertions(+), 48 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index eefc932bcc1..070d4288147 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -217,7 +217,7 @@ erpnext.PointOfSale.Controller = class { set_opening_entry_status() { this.page.set_title_sub( ` - + Opened at ${frappe.datetime.str_to_user(this.pos_opening_time)} ` diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index fe3b57a3694..950377f1b36 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -184,7 +184,7 @@ erpnext.PointOfSale.ItemCart = class { me.$totals_section.find(".edit-cart-btn").click(); } - const item_row_name = unescape($cart_item.attr("data-row-name")); + const item_row_name = $cart_item.attr("data-row-name"); me.events.cart_item_clicked({ name: item_row_name }); this.numpad_value = ""; }); @@ -464,10 +464,10 @@ erpnext.PointOfSale.ItemCart = class {
${this.get_customer_image()}
-
${customer_name}
+
${frappe.utils.escape_html(customer_name)}
${get_customer_description()}
-
+
@@ -484,11 +484,13 @@ erpnext.PointOfSale.ItemCart = class { if (!email_id && !mobile_no) { return `
${__("Click to add email / phone")}
`; } else if (email_id && !mobile_no) { - return `
${email_id}
`; + return `
${frappe.utils.escape_html(email_id)}
`; } else if (mobile_no && !email_id) { - return `
${mobile_no}
`; + return `
${frappe.utils.escape_html(mobile_no)}
`; } else { - return `
${email_id} - ${mobile_no}
`; + return `
${frappe.utils.escape_html( + email_id + )} - ${frappe.utils.escape_html(mobile_no)}
`; } } } @@ -496,9 +498,13 @@ erpnext.PointOfSale.ItemCart = class { get_customer_image() { const { customer, image } = this.customer_info || {}; if (image) { - return `
${image}
`; + return `
${frappe.utils.escape_html(image)}
`; } else { - return `
${frappe.get_abbr(customer)}
`; + return `
${frappe.utils.escape_html( + frappe.get_abbr(customer) + )}
`; } } @@ -559,7 +565,7 @@ erpnext.PointOfSale.ItemCart = class { .map((t) => { if (t.tax_amount_after_discount_amount == 0.0) return; return `
-
${t.description}
+
${frappe.utils.escape_html(t.description)}
${format_currency(t.tax_amount_after_discount_amount, currency)}
`; }) @@ -571,8 +577,9 @@ erpnext.PointOfSale.ItemCart = class { } get_cart_item({ name }) { - const item_selector = `.cart-item-wrapper[data-row-name="${escape(name)}"]`; - return this.$cart_items_wrapper.find(item_selector); + return this.$cart_items_wrapper.find(".cart-item-wrapper").filter(function () { + return $(this).attr("data-row-name") === name; + }); } get_item_from_frm(item) { @@ -602,7 +609,9 @@ erpnext.PointOfSale.ItemCart = class { if (!$item_to_update.length) { this.$cart_items_wrapper.append( - `
+ `
` ); $item_to_update = this.get_cart_item(item_data); @@ -612,7 +621,7 @@ erpnext.PointOfSale.ItemCart = class { `${get_item_image_html()}
- ${item_data.item_name} + ${frappe.utils.escape_html(item_data.item_name)}
${get_description_html()}
@@ -641,7 +650,7 @@ erpnext.PointOfSale.ItemCart = class { if (item_data.rate && item_data.amount && item_data.rate !== item_data.amount) { return `
-
${item_data.qty || 0} ${item_data.uom}
+
${item_data.qty || 0} ${frappe.utils.escape_html(item_data.uom)}
${format_currency(item_data.amount, currency)}
${format_currency(item_data.rate, currency)}
@@ -650,7 +659,7 @@ erpnext.PointOfSale.ItemCart = class { } else { return `
-
${item_data.qty || 0} ${item_data.uom}
+
${item_data.qty || 0} ${frappe.utils.escape_html(item_data.uom)}
${format_currency(item_data.rate, currency)}
@@ -671,7 +680,7 @@ erpnext.PointOfSale.ItemCart = class { } } item_data.description = frappe.ellipsis(item_data.description, 45); - return `
${item_data.description}
`; + return `
${frappe.utils.escape_html(item_data.description)}
`; } return ``; } @@ -683,22 +692,24 @@ erpnext.PointOfSale.ItemCart = class {
${frappe.get_abbr(item_name)} + src="${frappe.utils.escape_html(image)}" alt="${frappe.utils.escape_html(frappe.get_abbr(item_name))}">
`; } else { - return `
${frappe.get_abbr(item_name)}
`; + return `
${frappe.utils.escape_html( + frappe.get_abbr(item_name) + )}
`; } } } handle_broken_image($img) { - const item_abbr = $($img).attr("alt"); + const item_abbr = frappe.utils.escape_html($($img).attr("alt")); $($img).parent().replaceWith(`
${item_abbr}
`); } update_selector_value_in_cart_item(selector, value, item) { const $item_to_update = this.get_cart_item(item); - $item_to_update.attr(`data-${selector}`, escape(value)); + $item_to_update.attr(`data-${selector}`, value); } toggle_checkout_btn(show_checkout) { @@ -899,8 +910,8 @@ erpnext.PointOfSale.ItemCart = class {
${this.get_customer_image()}
-
${customer_name}
-
${customer}
+
${frappe.utils.escape_html(customer_name)}
+
${frappe.utils.escape_html(customer)}
@@ -1041,9 +1052,11 @@ erpnext.PointOfSale.ItemCart = class { }; transaction_container.append( - `
+ `
-
${invoice.name}
+
${frappe.utils.escape_html(invoice.name)}
${posting_datetime}
@@ -1051,7 +1064,7 @@ erpnext.PointOfSale.ItemCart = class { ${format_currency(invoice.grand_total, invoice.currency, frappe.sys_defaults.currency_precision) || 0}
- + ${__(invoice.status)}
diff --git a/erpnext/selling/page/point_of_sale/pos_item_details.js b/erpnext/selling/page/point_of_sale/pos_item_details.js index 75f6015980d..988ab60d548 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_details.js +++ b/erpnext/selling/page/point_of_sale/pos_item_details.js @@ -129,24 +129,26 @@ erpnext.PointOfSale.ItemDetails = class { return ``; } - this.$item_name.html(item_name); + this.$item_name.html(frappe.utils.escape_html(item_name)); this.$item_description.html(get_description_html()); this.$item_price.html(format_currency(price_list_rate, this.currency)); if (!this.hide_images && image) { this.$item_image.html( `${frappe.get_abbr(item_name)}` ); } else { - this.$item_image.html(`
${frappe.get_abbr(item_name)}
`); + this.$item_image.html( + `
${frappe.utils.escape_html(frappe.get_abbr(item_name))}
` + ); } } handle_broken_image($img) { - const item_abbr = $($img).attr("alt"); + const item_abbr = frappe.utils.escape_html($($img).attr("alt")); $($img).replaceWith(`
${item_abbr}
`); } diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index 1da8e1e5d65..f05040c6a08 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -196,10 +196,14 @@ erpnext.PointOfSale.ItemSelector = class { ${ !me.hide_images ? `
- ${format_currency(price_list_rate, item.currency, precision) || 0} / ${uom} + ${frappe.utils.escape_html(format_currency(price_list_rate, item.currency, precision)) || 0} / ${uom}
` : ` -
${format_currency(price_list_rate, item.currency, precision) || 0}
+
${ + frappe.utils.escape_html( + format_currency(price_list_rate, item.currency, precision) + ) || 0 + }
${uom}
${qty_to_display || "Non stock item"}
` diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_list.js b/erpnext/selling/page/point_of_sale/pos_past_order_list.js index 89bda039536..7eb5e16b2d6 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_list.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_list.js @@ -42,7 +42,7 @@ erpnext.PointOfSale.PastOrderList = class { this.$invoices_container.on("click", ".invoice-wrapper", function () { const invoice_clicked = $(this); const invoice_doctype = invoice_clicked.attr("data-invoice-doctype"); - const invoice_name = unescape(invoice_clicked.attr("data-invoice-name")); + const invoice_name = invoice_clicked.attr("data-invoice-name"); $(".invoice-wrapper").removeClass("invoice-selected"); invoice_clicked.addClass("invoice-selected"); @@ -108,15 +108,15 @@ erpnext.PointOfSale.PastOrderList = class { ); return `
+ }" data-invoice-name="${frappe.utils.escape_html(invoice.name)}">
- ${frappe.ellipsis(invoice.customer_name, 20)} + ${frappe.utils.escape_html(frappe.ellipsis(invoice.customer_name, 20))}
-
${invoice.name}
+
${frappe.utils.escape_html(invoice.name)}
${format_currency(invoice.grand_total, invoice.currency) || 0}
diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js index 4585b3307b2..d59b50c60ad 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js @@ -82,15 +82,19 @@ erpnext.PointOfSale.PastOrderSummary = class { return `
-
${doc.customer_name}
- ${is_customer_naming_by_customer_name ? `
${doc.customer}
` : ""} -
${this.customer_email}
+
${frappe.utils.escape_html(doc.customer_name)}
+ ${ + is_customer_naming_by_customer_name + ? `
${frappe.utils.escape_html(doc.customer)}
` + : "" + } +
${frappe.utils.escape_html(this.customer_email)}
-
${__("Sold by")}: ${doc.owner}
+
${__("Sold by")}: ${frappe.utils.escape_html(doc.owner)}
-
${doc.name}
+
${frappe.utils.escape_html(doc.name)}
${__(doc.status)}
`; } @@ -100,8 +104,8 @@ erpnext.PointOfSale.PastOrderSummary = class { return `
-
${item_data.item_name}
-
${item_data.qty || 0} ${item_data.uom}
+
${frappe.utils.escape_html(item_data.item_name)}
+
${item_data.qty || 0} ${frappe.utils.escape_html(item_data.uom)}
${get_rate_discount_html()}
@@ -166,7 +170,7 @@ erpnext.PointOfSale.PastOrderSummary = class { .map((t) => { return `
-
${t.description}
+
${frappe.utils.escape_html(t.description)}
${format_currency(t.tax_amount_after_discount_amount, doc.currency)}
`; @@ -185,7 +189,7 @@ erpnext.PointOfSale.PastOrderSummary = class { get_payment_html(doc, payment) { return `
-
${__(payment.mode_of_payment)}
+
${frappe.utils.escape_html(__(payment.mode_of_payment))}
${format_currency(payment.amount, doc.currency)}
`; } diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index a92c8958917..bf8c9f44049 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -519,7 +519,7 @@ erpnext.PointOfSale.Payment = class { return `
- ${p.mode_of_payment} + ${frappe.utils.escape_html(p.mode_of_payment)}
${amount}
@@ -603,7 +603,7 @@ erpnext.PointOfSale.Payment = class {
Redeem Loyalty Points
${amount}
-
${loyalty_program}
+
${frappe.utils.escape_html(loyalty_program)}
`