diff --git a/CHANGELOG b/CHANGELOG index e55dbac76c..63e2e6f262 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +* Do not resolve entities by default with lxml (issue11219) +* Use defusedxml to parse XML (issue11244) * Use dictionary as search_order on Reference field * Retry sending email on temporary failure * Order not sorted Selection by index definition diff --git a/setup.py b/setup.py index c4f76e43bd..dec4772a51 100755 --- a/setup.py +++ b/setup.py @@ -157,6 +157,7 @@ setup(name=name, license='GPL-3', python_requires='>=3.7', install_requires=[ + 'defusedxml', 'lxml >= 2.0', 'relatorio[fodt] >= 0.7.0', 'Genshi', diff --git a/trytond/__init__.py b/trytond/__init__.py index 249afc7ef7..407ebc4201 100644 --- a/trytond/__init__.py +++ b/trytond/__init__.py @@ -5,6 +5,8 @@ import time import warnings from email import charset +from lxml import etree, objectify + __version__ = "6.3.0" os.environ['TZ'] = 'UTC' @@ -16,3 +18,7 @@ if time.tzname[0] != 'UTC': # set email encoding for utf-8 to 'quoted-printable' charset.add_charset('utf-8', charset.QP, charset.QP) + +# prevent XML vulnerabilities by default +etree.set_default_parser(etree.XMLParser(resolve_entities=False)) +objectify.set_default_parser(objectify.makeparser(resolve_entities=False)) diff --git a/trytond/ir/translation.py b/trytond/ir/translation.py index 1d2b361281..f7b83b20a8 100644 --- a/trytond/ir/translation.py +++ b/trytond/ir/translation.py @@ -1,12 +1,12 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import os -import xml.dom.minidom from collections import defaultdict from difflib import SequenceMatcher from io import BytesIO import polib +from defusedxml.minidom import parseString from genshi.filters.i18n import extract as genshi_extract from lxml import etree from relatorio.reporting import MIMETemplateLoader @@ -958,7 +958,7 @@ class TranslationSet(Wizard): zip_.read('content.xml'), zip_.read('styles.xml'), ]: - document = xml.dom.minidom.parseString(content_xml) + document = parseString(content_xml) for string in extract(document.documentElement): yield string diff --git a/trytond/ir/ui/view.py b/trytond/ir/ui/view.py index 81a6952474..f7b22c0d4c 100644 --- a/trytond/ir/ui/view.py +++ b/trytond/ir/ui/view.py @@ -241,7 +241,7 @@ class View(ModelSQL, ModelView): ]) views.sort( key=lambda v: self._module_index.get(v.module, -1), reverse=True) - parser = etree.XMLParser(remove_comments=True) + parser = etree.XMLParser(remove_comments=True, resolve_entities=False) tree = etree.fromstring(arch, parser=parser) decoder = PYSONDecoder({'context': Transaction().context}) for view in views: diff --git a/trytond/model/modelview.py b/trytond/model/modelview.py index f36f1e9ff3..45a89e600f 100644 --- a/trytond/model/modelview.py +++ b/trytond/model/modelview.py @@ -250,7 +250,8 @@ class ModelView(Model): level = 1 if result['type'] == 'tree' else 0 # Update arch and compute fields from arch - parser = etree.XMLParser(remove_blank_text=True) + parser = etree.XMLParser( + remove_blank_text=True, resolve_entities=False) tree = etree.fromstring(result['arch'], parser) result['arch'], result['fields'] = cls.parse_view( tree, result['type'], view_id=view_id, diff --git a/trytond/protocols/xmlrpc.py b/trytond/protocols/xmlrpc.py index 7cb5c53058..4ed0e8dc3d 100644 --- a/trytond/protocols/xmlrpc.py +++ b/trytond/protocols/xmlrpc.py @@ -6,6 +6,7 @@ import xmlrpc.client as client # convert decimal to float before marshalling: from decimal import Decimal +import defusedxml.xmlrpc from werkzeug.exceptions import ( BadRequest, Conflict, Forbidden, InternalServerError, Locked, TooManyRequests) @@ -20,6 +21,8 @@ from trytond.tools import cached_property logger = logging.getLogger(__name__) +defusedxml.xmlrpc.monkey_patch() + def dump_decimal(self, value, write): write('')