From f244c5c7ef96a490adf9109437525baf8f727417 Mon Sep 17 00:00:00 2001 From: yosa-odoo Date: Wed, 13 Mar 2024 14:21:29 +0100 Subject: [PATCH] [FIX] tools: remove control characters xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Steps to reproduce: [account_edi_ubl_cii] - create an invoice and set a line with on the control character https://unicode-explorer.com/b/0000 - confirm it - try to print it Issue: Ugly Stack Trace Cause: XML does not accept such characters ``` The characters to be escaped are the control characters #x0 to #x1F and #x7F (most of which cannot appear in XML) [...] XML processors must accept any character in the range specified for Char: `Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]` source:https://www.w3.org/TR/xml/ ``` opw-3773808 closes odoo/odoo#163433 X-original-commit: d06a22991cd604e46d6392f6394b2b0e6a4ae673 Signed-off-by: William André (wan) --- odoo/tools/xml_utils.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/odoo/tools/xml_utils.py b/odoo/tools/xml_utils.py index 1d1713d8125..5a661031d80 100644 --- a/odoo/tools/xml_utils.py +++ b/odoo/tools/xml_utils.py @@ -2,6 +2,7 @@ """Utilities for generating, parsing and checking XML/XSD files on top of the lxml.etree module.""" import logging +import re import requests import zipfile from io import BytesIO @@ -14,6 +15,27 @@ from odoo.exceptions import UserError _logger = logging.getLogger(__name__) +def remove_control_characters(byte_node): + """ + The characters to be escaped are the control characters #x0 to #x1F and #x7F (most of which cannot appear in XML) + [...] XML processors must accept any character in the range specified for Char: + `Char :: = #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]` + source:https://www.w3.org/TR/xml/ + """ + return re.sub( + '[^' + '\u0009' + '\u000A' + '\u000D' + '\u0020-\uD7FF' + '\uE000-\uFFFD' + '\U00010000-\U0010FFFF' + ']'.encode(), + b'', + byte_node, + ) + + class odoo_resolver(etree.Resolver): """Odoo specific file resolver that can be added to the XML Parser. @@ -118,7 +140,7 @@ def cleanup_xml_node(xml_node_or_string, remove_blank_text=True, remove_blank_no if isinstance(xml_node, str): xml_node = xml_node.encode() # misnomer: fromstring actually reads bytes if isinstance(xml_node, bytes): - xml_node = etree.fromstring(xml_node) + xml_node = etree.fromstring(remove_control_characters(xml_node)) # Process leaf nodes iteratively # Depth-first, so any inner node may become a leaf too (if children are removed)