From 3762484f4b890ea7b35846003c7256a5fee20038 Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Mon, 26 Jan 2026 10:12:03 +0000 Subject: [PATCH] Bug 41709: Fix EDIfact special character escaping in GIR segments and other fields Apply encode_text function to all text fields in EDIfact Order messages to properly escape special characters (?, ', :, +) that could break message parsing. Previously only bibliographic description fields were escaped, but GIR segments containing collection codes, branch codes, item types, call numbers, and other data were vulnerable. Test plan: - Verify all existing tests pass - Added comprehensive tests for special character escaping in GIR segments - Confirmed that fields containing ?, ', :, + are properly escaped Sponsored-by: OpenFifth --- Koha/Edifact/Order.pm | 22 +++++++++++----------- t/Ediorder.t | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/Koha/Edifact/Order.pm b/Koha/Edifact/Order.pm index c6c98cee842..25859b17d98 100644 --- a/Koha/Edifact/Order.pm +++ b/Koha/Edifact/Order.pm @@ -465,7 +465,7 @@ sub order_line { # FTX free text for current orderline # Pass vendor note in FTX free text segment if ( $orderline->order_vendornote ) { - my $vendornote = $orderline->order_vendornote; + my $vendornote = encode_text( $orderline->order_vendornote ); chomp $vendornote; my $ftx = 'FTX+LIN+++'; $ftx .= $vendornote; @@ -489,7 +489,7 @@ sub order_line { # RFF : suppliers unique quotation reference number if ( $orderline->suppliers_reference_number ) { $rff = join q{}, 'RFF+', $orderline->suppliers_reference_qualifier, - ':', $orderline->suppliers_reference_number, $seg_terminator; + ':', encode_text( $orderline->suppliers_reference_number ), $seg_terminator; $self->add_seg($rff); } @@ -576,7 +576,7 @@ sub gir_segments { my $orderfields = $params->{ol_fields}; my @onorderitems = @{ $params->{items} }; - my $budget_code = $orderfields->{budget_code}; + my $budget_code = encode_text( $orderfields->{budget_code} ); my @segments; my $sequence_no = 1; my $lsq_field = C4::Context->preference('EdifactLSQ'); @@ -590,27 +590,27 @@ sub gir_segments { } if ( $item->{branchcode} ) { push @gir_elements, - { identity_number => 'LLO', data => $item->{branchcode} }; + { identity_number => 'LLO', data => encode_text( $item->{branchcode} ) }; } if ( $item->{itype} ) { push @gir_elements, - { identity_number => 'LST', data => $item->{itype} }; + { identity_number => 'LST', data => encode_text( $item->{itype} ) }; } # Handle LSQ mapping if ( $lsq_field && $item->{$lsq_field} ) { push @gir_elements, - { identity_number => 'LSQ', data => $item->{$lsq_field} }; + { identity_number => 'LSQ', data => encode_text( $item->{$lsq_field} ) }; } # Handle LSL mapping if ( $lsl_field && $item->{$lsl_field} ) { push @gir_elements, - { identity_number => 'LSL', data => $item->{$lsl_field} }; + { identity_number => 'LSL', data => encode_text( $item->{$lsl_field} ) }; } if ( $item->{itemcallnumber} ) { push @gir_elements, - { identity_number => 'LSM', data => $item->{itemcallnumber} }; + { identity_number => 'LSM', data => encode_text( $item->{itemcallnumber} ) }; } # itemcallnumber -> shelfmark @@ -618,7 +618,7 @@ sub gir_segments { push @gir_elements, { identity_number => 'LVT', - data => $orderfields->{servicing_instruction} + data => encode_text( $orderfields->{servicing_instruction} ) }; } my $e_cnt = 0; # count number of elements so we dont exceed 5 per segment @@ -663,7 +663,7 @@ sub lin_segment { my ( $line_number, $item_number_id ) = @_; if ($item_number_id) { - $item_number_id = "++${item_number_id}:EN"; + $item_number_id = "++" . encode_text($item_number_id) . ":EN"; } else { $item_number_id = q||; } @@ -688,7 +688,7 @@ sub additional_product_id { } # function id set to 5 states this is the main product id - return "PIA+5+$product_id:$product_code$seg_terminator"; + return "PIA+5+" . encode_text($product_id) . ":$product_code$seg_terminator"; } sub message_date_segment { diff --git a/t/Ediorder.t b/t/Ediorder.t index c8f07a788fb..eadcfa0aeac 100755 --- a/t/Ediorder.t +++ b/t/Ediorder.t @@ -4,7 +4,7 @@ use warnings; use FindBin qw( $Bin ); use Test::NoWarnings; -use Test::More tests => 14; +use Test::More tests => 16; use t::lib::Mocks; BEGIN { use_ok('Koha::Edifact::Order') } @@ -136,3 +136,35 @@ cmp_ok( $gsegs[3], 'eq', q{GIR+002+S_I:LVT}, 'Second part of split GIR field OK' ); + +# Test that special characters are properly escaped in GIR segments +my $special_orderfields = { + budget_code => 'BUDGET?+', + servicing_instruction => "Note with 'special' chars:", +}; +my @special_items = ( + { + itype => 'TYPE?:', + location => 'LOC+TION', + itemcallnumber => 'CALL?' . q{'}, + branchcode => 'BRANCH:+', + } +); + +my @special_gsegs = Koha::Edifact::Order::gir_segments( + { + ol_fields => $special_orderfields, + items => \@special_items + } +); + +cmp_ok( + $special_gsegs[0], 'eq', + q{GIR+001+BUDGET???+:LFN+BRANCH?:?+:LLO+TYPE???::LST+LOC?+TION:LSQ+CALL???':LSM}, + 'GIR segment with special characters properly escaped' +); + +cmp_ok( + $special_gsegs[1], 'eq', q{GIR+001+Note with ?'special?' chars?::LVT}, + 'GIR segment with servicing instruction special characters properly escaped' +); -- 2.52.0