Bug 37727: Prevent CSV Formula injection via DataTables

This change prevents CSV Formula injection on DataTables exports
by escaping formula with a single quote prefix as per OWASP recommendations.

Test plan:
0. Apply patch
1. Go to http://localhost:8081/cgi-bin/koha/members/memberentry.pl
?op=edit_form&destination=circ&borrowernumber=51
2. Add the following in a "Circulation note"
=SUM(1+1)
3. Go to http://localhost:8081/cgi-bin/koha/members/member.pl
?quicksearch=1&circsearch=1&searchmember=koha
4. Click "Export" and choose "Excel" and "CSV"
5. Open those downloaded files in Excel
6. Note that the =SUM(1+1) function is prefixed with a single quote,
and is not automatically executed

Signed-off-by: Magnus Enger <magnus@libriotech.no>
Works as advertised. The problematic "cell" is exported as "'=SUM(1+1)".
Signed-off-by: Chris Cormack <chris@bigballofwax.co.nz>
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
This commit is contained in:
David Cook 2024-10-17 05:35:56 +00:00 committed by Paul Derscheid
parent 9bbb73fa26
commit e779a5d760
No known key found for this signature in database
GPG Key ID: C25F9FD44CED595B
2 changed files with 38 additions and 4 deletions

View File

@ -72,6 +72,23 @@ function KohaTable(id_selector, dt_parameters, table_settings, add_filters, defa
exportColumns = dt_parameters["exportColumns"];
}
const export_format_spreadsheet = {
body: function ( data, row, column, node ) {
var newnode = $(node);
if ( newnode.find(".noExport").length > 0 ) {
newnode = newnode.clone();
newnode.find(".noExport").remove();
}
let trimmed_str = newnode.text().replace( /\n/g, ' ' ).trim();
const unsafeCharacters = /^[=+\-@\t\r]/;
if ( unsafeCharacters.test(trimmed_str) ){
trimmed_str = "'" + trimmed_str;
}
return trimmed_str;
}
}
var export_format = {
body: function ( data, row, column, node ) {
var newnode = $(node);
@ -115,7 +132,7 @@ function KohaTable(id_selector, dt_parameters, table_settings, add_filters, defa
exportOptions: {
columns: exportColumns,
rows: exportRows,
format: export_format
format: export_format_spreadsheet
},
},
{
@ -137,7 +154,7 @@ function KohaTable(id_selector, dt_parameters, table_settings, add_filters, defa
exportOptions: {
columns: exportColumns,
rows: exportRows,
format: export_format
format: export_format_spreadsheet
},
}
);

View File

@ -605,6 +605,23 @@ function _dt_buttons(params){
exportColumns = settings["exportColumns"];
}
const export_format_spreadsheet = {
body: function ( data, row, column, node ) {
var newnode = $(node);
if ( newnode.find(".noExport").length > 0 ) {
newnode = newnode.clone();
newnode.find(".noExport").remove();
}
let trimmed_str = newnode.text().replace( /\n/g, ' ' ).trim();
const unsafeCharacters = /^[=+\-@\t\r]/;
if ( unsafeCharacters.test(trimmed_str) ){
trimmed_str = "'" + trimmed_str;
}
return trimmed_str;
}
}
var export_format = {
body: function ( data, row, column, node ) {
var newnode = $(node);
@ -623,14 +640,14 @@ function _dt_buttons(params){
extend: 'excelHtml5',
exportOptions: {
columns: exportColumns,
format: export_format
format: export_format_spreadsheet
},
},
{
extend: 'csvHtml5',
exportOptions: {
columns: exportColumns,
format: export_format
format: export_format_spreadsheet
},
},
{