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 <https://openfifth.co.uk/>
Signed-off-by: Laura_Escamilla <laura.escamilla@bywatersolutions.com>

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This commit is contained in:
Martin Renvoize 2026-01-26 10:12:03 +00:00 committed by Pedro Amorim
parent b302481a4d
commit 38cbe6b195
No known key found for this signature in database
GPG Key ID: 06B75BA7F3894D55
2 changed files with 44 additions and 12 deletions

View File

@ -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 {

View File

@ -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'
);