From 8ddf20dec91cf1a678f1e153804d45632c083ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 1 Mar 2022 19:07:56 +0100 Subject: [PATCH] Protect against XML vulnerabilities issue11219 issue11244 (grafted from a64ca55b86e3462b5d5348c4fc8698350695b60e) (grafted from 2ef295408a0bdc568453ca78fd564ddd48a28131) --HG-- branch : 6.0 --- CHANGELOG | 3 +++ setup.py | 1 + trytond/__init__.py | 6 ++++++ trytond/ir/translation.py | 4 ++-- trytond/model/modelview.py | 5 +++-- trytond/protocols/xmlrpc.py | 3 +++ 6 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f063d3433c..db9e54e705 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +* Do not resolve entities by default with lxml (issue11219) +* Use defusedxml to parse XML (issue11244) + Version 6.0.15 - 2022-02-16 * Bug fixes (see mercurial logs for details) diff --git a/setup.py b/setup.py index 20a4cb3d0b..b1eee37d36 100755 --- a/setup.py +++ b/setup.py @@ -156,6 +156,7 @@ setup(name=name, license='GPL-3', python_requires='>=3.6', install_requires=[ + 'defusedxml', 'lxml >= 2.0', 'relatorio[fodt] >= 0.7.0', 'Genshi', diff --git a/trytond/__init__.py b/trytond/__init__.py index 9f1e33f38f..8aad92e819 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.0.16" 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 7cb2a02f4c..b79ba5bbcd 100644 --- a/trytond/ir/translation.py +++ b/trytond/ir/translation.py @@ -1,7 +1,6 @@ # 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 difflib import SequenceMatcher from collections import defaultdict from io import BytesIO @@ -13,6 +12,7 @@ from sql.functions import Substring, Position from sql.conditionals import Case from sql.aggregate import Max +from defusedxml.minidom import parseString from genshi.filters.i18n import extract as genshi_extract from relatorio.reporting import MIMETemplateLoader from relatorio.templates.opendocument import get_zip_file @@ -959,7 +959,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/model/modelview.py b/trytond/model/modelview.py index fbf45fd72f..8bfd83b348 100644 --- a/trytond/model/modelview.py +++ b/trytond/model/modelview.py @@ -319,7 +319,7 @@ class ModelView(Model): if not result['arch']: raise ValueError("Missing view architecture for %s" % (( cls.__name__, view_id, view_type),)) - parser = etree.XMLParser(remove_comments=True) + parser = etree.XMLParser(remove_comments=True, resolve_entities=False) tree = etree.fromstring(result['arch'], parser=parser) for view in views: if view.domain: @@ -374,7 +374,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'], result['field_childs'], level=level) diff --git a/trytond/protocols/xmlrpc.py b/trytond/protocols/xmlrpc.py index e4cbcafa16..9d2450ccef 100644 --- a/trytond/protocols/xmlrpc.py +++ b/trytond/protocols/xmlrpc.py @@ -7,6 +7,7 @@ import logging # convert decimal to float before marshalling: from decimal import Decimal +import defusedxml.xmlrpc from werkzeug.wrappers import Response from werkzeug.exceptions import ( BadRequest, InternalServerError, Conflict, Forbidden, Locked, @@ -21,6 +22,8 @@ from trytond.tools import cached_property logger = logging.getLogger(__name__) +defusedxml.xmlrpc.monkey_patch() + def dump_decimal(self, value, write): value = {'__class__': 'Decimal',