From 5924707c1f4444dbdfa4c77aaccfa4ced2b4468b Mon Sep 17 00:00:00 2001 From: David Cook Date: Tue, 29 Jul 2025 04:07:37 +0000 Subject: [PATCH] [22.11.x] Bug 40538: Fix XSS in suspend hold modal This patch fixes a XSS vulnerability in the suspend hold modal in the staff interface. By building our Javascript using Web APIs rather than with strings and jQuery's html() method, we're able to securely handle malicious payloads. Test plan: 0. Apply the patch 1. Go to http://localhost:8081/cgi-bin/koha/reserve/request.pl?biblionumber=29 2. Add a hold for "koha" patron 3. Go to http://localhost:8081/cgi-bin/koha/circ/circulation.pl?borrowernumber=51 4. Click "Holds" tab and click "Suspend" 5. Note the modal loads properly 6. Go to http://localhost:8081/cgi-bin/koha/members/moremember.pl?borrowernumber=51 7. Click "Holds" tab and click "Suspend" 8. Note the modal loads properly Signed-off-by: Pedro Amorim Signed-off-by: Marcel de Rooy Bug 40538: Simplify code Signed-off-by: Marcel de Rooy --- koha-tmpl/intranet-tmpl/prog/js/holds.js | 70 +++++++++++++++++------- 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/koha-tmpl/intranet-tmpl/prog/js/holds.js b/koha-tmpl/intranet-tmpl/prog/js/holds.js index 6a156f85857..fe209d4da8b 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/holds.js +++ b/koha-tmpl/intranet-tmpl/prog/js/holds.js @@ -251,20 +251,48 @@ $(document).ready(function() { "mDataProp": function( oObj ) { holds[oObj.reserve_id] = oObj; //Store holds for later use - if ( oObj.found ) { - return ""; - } else if ( oObj.suspend == 1 ) { - return "" - +" " + __("Resume") + ""; - } else { - return "" - +" " + __("Suspend") + ""; - } - } - }, - { - "mDataProp": function( oObj ) { - var data = ""; + if (oObj.found) { + return ""; + } else if (oObj.suspend == 1) { + return ( + "" + + " " + + __("Resume") + + "" + ); + } else { + const link = Object.assign( + document.createElement("a"), + { + className: + "hold-suspend btn btn-default btn-xs", + textContent: " " + __("Suspend"), + } + ); + link.setAttribute( + "data-hold-id", + oObj.reserve_id + ); + link.setAttribute( + "data-hold-title", + oObj.title + ); + const icon = Object.assign( + document.createElement("i"), + { + className: "fa fa-pause", + } + ); + link.prepend(icon); + return link.outerHTML; + } + }, + }, + { + data: function (oObj) { + var data = ""; if ( oObj.suspend == 1 ) { data += "

" + __("Hold is suspended"); @@ -322,13 +350,13 @@ $(document).ready(function() { }, })); - $('#holds-table').on( 'draw.dt', function () { - $(".hold-suspend").on( "click", function() { - var hold_id = $(this).data('hold-id'); - var hold_title = $(this).data('hold-title'); - $("#suspend-modal-title").html( hold_title ); - $("#suspend-modal-submit").data( 'hold-id', hold_id ); - $('#suspend-modal').modal('show'); + $("#holds-table").on("draw.dt", function () { + $(".hold-suspend").on("click", function () { + var hold_id = $(this).data("hold-id"); + var hold_title = $(this).data("hold-title"); + $("#suspend-modal-title").text(hold_title); + $("#suspend-modal-submit").data("hold-id", hold_id); + $("#suspend-modal").modal("show"); }); $(".hold-resume").on("click", function () { -- 2.51.0