From 684d4effd426089321068ff1497145e4e4a60687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Chaves?= Date: Wed, 29 Nov 2023 11:54:08 +0100 Subject: [PATCH] Set an arbitrary upper limit on ReDoS-vulnerable regexps --- docs/news.rst | 14 ++++++++++++++ scrapy/utils/iterators.py | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/news.rst b/docs/news.rst index 4c4110306..aba35aa46 100644 --- a/docs/news.rst +++ b/docs/news.rst @@ -6,6 +6,20 @@ Release notes .. note:: Scrapy 1.x is the last series supporting Python 2. Scrapy 2.x supports **Python 3 only**. +.. _release-1.8.4: + +Scrapy 1.8.4 (unreleased) +------------------------- + +**Security bug fix:** + +- Fixed regular expressions susceptible to a `ReDoS attack`_ affecting the + ``iternodes`` node iterator of :class:`~scrapy.spiders.XMLFeedSpider`. + Please, see the `cc65-xxvf-f7r9 security advisory`_ for more information. + + .. _ReDoS attack: https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS + .. _cc65-xxvf-f7r9 security advisory: https://github.com/scrapy/scrapy/security/advisories/GHSA-cc65-xxvf-f7r9 + .. _release-1.8.3: Scrapy 1.8.3 (2022-07-25) diff --git a/scrapy/utils/iterators.py b/scrapy/utils/iterators.py index a12e14005..feaf4812e 100644 --- a/scrapy/utils/iterators.py +++ b/scrapy/utils/iterators.py @@ -26,7 +26,7 @@ def xmliter(obj, nodename): """ nodename_patt = re.escape(nodename) - HEADER_START_RE = re.compile(r'^(.*?)<\s*%s(?:\s|>)' % nodename_patt, re.S) + HEADER_START_RE = re.compile(r'^(.{,1024}?)<\s*%s(?:\s|>)' % nodename_patt, re.S) HEADER_END_RE = re.compile(r'<\s*/%s\s*>' % nodename_patt, re.S) text = _body_or_str(obj) @@ -35,7 +35,7 @@ def xmliter(obj, nodename): header_end = re_rsearch(HEADER_END_RE, text) header_end = text[header_end[1]:].strip() if header_end else '' - r = re.compile(r'<%(np)s[\s>].*?' % {'np': nodename_patt}, re.DOTALL) + r = re.compile(r'<%(np)s[\s>].{,1024}?' % {'np': nodename_patt}, re.DOTALL) for match in r.finditer(text): nodetext = header_start + match.group() + header_end yield Selector(text=nodetext, type='xml').xpath('//' + nodename)[0]