QWED
v7.2.2 GitHub 60

Type to search pages, engines and advisories.

    Looking for API details?Search the docs

    GHSA-mrrj-6m2q-jch9

    ISO 20022 business rules read a different amount and currency than the parsed XML, allowing over-limit and disallowed-currency payments to pass

    Impact

    The ISO 20022 business-rules engine in qwed-finance (CrossGuard.verify_iso20022_with_rules, also used by UCPIntegration.verify_iso20022_payment) extracts the settlement amount and currency from the raw XML text with a regular expression, instead of from the parsed XML tree that MessageGuard validates. An XML document can therefore show one amount and currency to the rules engine while every XML parser — and any downstream payment system — reads another.

    Two constructs make the rules engine read a decoy:

    • the operative element uses a namespace prefix containing a character that is a valid XML NameChar but outside the regex prefix class [\w.-] (for example U+00B7), so the regex never sees it;
    • a decoy amount element is placed inside an XML processing instruction (<?...?>); comments and CDATA are stripped before matching, but processing instructions are not.

    Reproduced on v3.0.0 with business rules max_amount=1000, allowed_currencies=["USD"]:

    Document Parsers read Rules engine result
    plain IntrBkSttlmAmt Ccy="RUB" 9,999,999.00 RUB 9,999,999.00 rejected (both rules)
    same amount behind a prefixed element plus a PI decoy USD 100.00 RUB 9,999,999.00 passed=True, no violations

    Through UCPIntegration(max_transaction_amount=1000, allowed_currencies=["USD"]).verify_iso20022_payment(...) the same document returns PaymentStatus.APPROVED, can_proceed=True.

    Who is affected: applications that rely on verify_iso20022_with_rules or UCPIntegration.verify_iso20022_payment to enforce amount limits or currency allow-lists on ISO 20022 (pacs.008) messages produced by an LLM or another untrusted source. The npm SDK does not expose these code paths.

    Root cause

    cross_guard.py (_AMOUNT_ELEMENT_RE, _extract_xml_occurrences) performs regex extraction over raw document text. Earlier fixes (comment/CDATA stripping, all-occurrence matching, \w prefixes) narrowed the gap but kept a second, regex-based view of the document that can disagree with the XML tree.

    Patches

    Fixed in qwed-finance 3.0.1 (release). Upgrade with pip install -U qwed-finance==3.0.1.

    The business-rules engine now takes IntrBkSttlmAmt amounts and Ccy currencies from the defusedxml-parsed XML tree (matched by local name in any namespace), not from a regular expression over raw text. In addition:

    • an amount element containing child nodes (elements, comments, processing instructions) fails closed as unparseable;
    • amounts must be a plain xs:decimal — exponents and grouping separators are rejected;
    • character references are decoded exactly once, by the parser.

    Workarounds

    • Extract the amount and currency yourself from the parsed XML tree (by namespace-qualified local name) and enforce limits on those values; do not rely on verify_iso20022_with_rules for limit enforcement.
    • Reject documents that contain processing instructions or non-default namespace prefixes on amount elements before verification.

    References

    Reproduced on qwed-finance 3.0.0 (bb0cdf3).