From ec19d35ca9480c56b5284e30158c3af2a7bd7212 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 Signed-off-by: Laura_Escamilla Signed-off-by: Kyle M Hall Signed-off-by: Lucas Gass --- 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 b57c74f4eb..dc753e2f88 100644 --- a/Koha/Edifact/Order.pm +++ b/Koha/Edifact/Order.pm @@ -468,7 +468,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; @@ -492,7 +492,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); } @@ -579,7 +579,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'); @@ -593,27 +593,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 @@ -621,7 +621,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 @@ -666,7 +666,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||; } @@ -691,7 +691,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 c8f07a788f..eadcfa0aea 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' +);