From acee05423a2d44d75a717787e481b6366023b747 Mon Sep 17 00:00:00 2001 From: David Cook Date: Tue, 29 Jul 2025 04:07:37 +0000 Subject: [PATCH] [24.05.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 e7a4ffa8293..c23a48da2cc 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/holds.js +++ b/koha-tmpl/intranet-tmpl/prog/js/holds.js @@ -256,20 +256,48 @@ $(document).ready(function() { "data": 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") + ""; - } - } - }, - { - "data": 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"); @@ -330,13 +358,13 @@ $(document).ready(function() { }, }, table_settings_holds_table ); - $('#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