https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=43694

            Bug ID: 43694
           Summary: Koha::REST::V1::to_xml emits child elements in random
                    order, producing schema-invalid XML
   Initiative type: ---
        Sponsorship ---
            status:
           Product: Koha
           Version: Main
          Hardware: All
                OS: All
            Status: NEW
          Severity: major
          Priority: P5 - low
         Component: REST API
          Assignee: [email protected]
          Reporter: [email protected]
        QA Contact: [email protected]
                CC: [email protected]
  Target Milestone: ---

Koha::REST::V1::to_xml builds XML by walking Perl hash keys:

    sub to_xml {                                  # V1.pm line 240
        my $root_key = ( keys %$json )[0];        # line 244
        ...
    sub _json_to_xml {                            # line 261
        foreach my $key ( keys %$json ) {         # line 265

Perl randomises hash key order per process (PERL_PERTURB_KEYS, default since
5.18), so the order of child elements differs from one run to the next. The ISO
18626 schema uses xs:sequence, not xs:all, so element order is significant: a
message whose children are emitted in the wrong order is invalid and a
conformant receiver rejects it at parse time.

The same applies to the root element: line 244 takes ( keys %$json )[0], which
is only correct because these payloads happen to have exactly one top-level
key. It is fragile for the same reason.

How to reproduce:

Run this five times on a Koha instance and compare the output lines.

    use Modern::Perl;
    use XML::LibXML;
    use Koha::REST::V1;

    my $json = {
        supplyingAgencyMessage => {
            header => {
                supplyingAgencyId         => { agencyIdType => 'ISIL',
agencyIdValue => 'X' },
                requestingAgencyId        => { agencyIdType => 'ISIL',
agencyIdValue => 'Y' },
                timestamp                 => '2026-09-30T00:00:00Z',
                requestingAgencyRequestId => '1',
                supplyingAgencyRequestId  => '2',
            },
            messageInfo => { reasonForMessage => 'StatusChange' },
            statusInfo  => { status => 'Loaned', lastChange =>
'2026-09-30T00:00:00Z' },
        },
    };

    my $doc  = XML::LibXML->new->parse_string( Koha::REST::V1::to_xml($json) );
    my $root = $doc->documentElement;
    say join( ', ', map { $_->nodeName } $root->findnodes('./*') );

Observed on Koha 26.05.02, Perl v5.38.2 — five consecutive runs:

    header, statusInfo, messageInfo
    header, messageInfo, statusInfo
    messageInfo, header, statusInfo
    statusInfo, header, messageInfo
    messageInfo, statusInfo, header

The schema requires header, messageInfo, statusInfo, deliveryInfo, returnInfo
in that order (ISO-18626-v1_2.xsd), so only the second run above is valid.
Nested elements are scrambled the same way: header itself must be
supplyingAgencyId, requestingAgencyId, multipleItemRequestId, timestamp,
requestingAgencyRequestId, supplyingAgencyRequestId,
requestingAgencyAuthentication.

Why this has not been noticed:

1. ISO 18626 testing so far appears to have been Koha-to-Koha. The inbound
side,   Koha::REST::V1::parse_xml/_parse_node, builds a hash and therefore does
not care   about element order, so two Koha instances understand each other
whatever order they   emit.

2. Koha does validate these payloads — Koha::ILL::ISO18626::is_invalid resolves
the   swagger definition and calls $schema->validate($json). But that validates
the JSON structure, and JSON objects are unordered by definition, so the check
passes while   the XML that goes out on the wire is invalid. The validation
gives false confidence   here rather than catching the problem.

This makes the defect worse than an ordinary bug: it is not deterministic.
Testing an integration once and seeing it work says nothing, because the next
message may be
ordered differently.

How it was found:

Interoperability testing between Koha 26.05.02 and a third-party ISO 18626
implementation written in .NET, generated directly from the official v1.2 XSD
(https://illtransactions.org/schemas/ISO-18626-v1_2.xsd). The first
supplyingAgencyMessage
Koha sent was rejected at XML deserialisation; retrying produced a different
failure
position, which is what led to the hash ordering.

Suggested fix:

to_xml has no way to know the required order on its own, so it needs the order
to come from somewhere. Two options that both avoid changing every caller:

  a. Derive the order from the OpenAPI definition that already describes each
message.
     JSON::Validator keeps the properties in the order they appear in the spec
file, so the swagger definition can supply the sequence. This also keeps the
spec as the single source of truth.

  b. Let callers pass an explicit ordering, e.g. to_xml($json, { order => {...}
}), and have the ISO 18626 code supply the sequences from the XSD.

Whichever is chosen, it is worth adding a test that serialises a known payload
and
asserts the resulting XML validates against the official XSD — a JSON-level
check cannot catch this class of bug.

Related: bug 43674 (ISO 18626 messages are missing the mandatory
ISO18626Message
envelope), also in Koha::REST::V1. Fixing either one alone still leaves Koha
unable to exchange ISO 18626 messages with a conformant implementation.

-- 
You are receiving this mail because:
You are watching all bug changes.
You are the assignee for the bug.
_______________________________________________
Koha-bugs mailing list -- [email protected]
To unsubscribe send an email to [email protected]
website : http://www.koha-community.org/
git : http://git.koha-community.org/
bugs : http://bugs.koha-community.org/

Reply via email to