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 <pedro.amorim@openfifth.co.uk>

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>

Bug 40538: Simplify code

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Jesse Maseto <jesse@bywatersolutions.com>
This commit is contained in:
David Cook 2025-07-29 04:07:37 +00:00 committed by Jesse Maseto
parent fa080e6b00
commit 81ef0aaccc
No known key found for this signature in database
GPG Key ID: 4D16BFCF78FFFDEC
1 changed files with 49 additions and 21 deletions

View File

@ -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 "<a class='hold-resume btn btn-default btn-xs' data-hold-id='" + oObj.reserve_id + "'>"
+"<i class='fa fa-play'></i> " + __("Resume") + "</a>";
} else {
return "<a class='hold-suspend btn btn-default btn-xs' data-hold-id='" + oObj.reserve_id + "' data-hold-title='"+ oObj.title +"'>"
+"<i class='fa fa-pause'></i> " + __("Suspend") + "</a>";
}
}
},
{
"data": function( oObj ) {
var data = "";
if (oObj.found) {
return "";
} else if (oObj.suspend == 1) {
return (
"<a class='hold-resume btn btn-default btn-xs' data-hold-id='" +
oObj.reserve_id +
"'>" +
"<i class='fa fa-play'></i> " +
__("Resume") +
"</a>"
);
} 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 += "<p>" + __("Hold is <strong>suspended</strong>");
@ -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 () {