diff --git a/scrapy/utils/response.py b/scrapy/utils/response.py
index 8401d4ed1..4369e6439 100644
--- a/scrapy/utils/response.py
+++ b/scrapy/utils/response.py
@@ -103,9 +103,9 @@ def open_in_browser(
body = response.body
if isinstance(response, HtmlResponse):
if b"'
- body = re.sub(b"", b"", body, flags=re.DOTALL)
- body = re.sub(rb"(
|\s.{,1024}?>))", to_bytes(repl), body)
+ repl = rf'\0'
+ body = re.sub(b"(?s)|$)", b"", body)
+ body = re.sub(rb"]*?>)", to_bytes(repl), body, count=1)
ext = ".html"
elif isinstance(response, TextResponse):
ext = ".txt"
diff --git a/tests/test_utils_response.py b/tests/test_utils_response.py
index 80e15a60f..942584d92 100644
--- a/tests/test_utils_response.py
+++ b/tests/test_utils_response.py
@@ -1,10 +1,12 @@
import unittest
import warnings
from pathlib import Path
+from time import process_time
from urllib.parse import urlparse
from scrapy.exceptions import ScrapyDeprecationWarning
from scrapy.http import HtmlResponse, Response, TextResponse
+from scrapy.settings.default_settings import DOWNLOAD_MAXSIZE
from scrapy.utils.python import to_bytes
from scrapy.utils.response import (
get_base_url,
@@ -198,3 +200,37 @@ class ResponseUtilsTest(unittest.TestCase):
assert open_in_browser(
r5, _openfunc=check_base_url
), "Inject unique base url with conditional comment"
+
+ def test_open_in_browser_redos_comment(self):
+ MAX_CPU_TIME = 30
+
+ # Exploit input from
+ # https://makenowjust-labs.github.io/recheck/playground/
+ # for // (old pattern to remove comments).
+ body = b"->"
+
+ response = HtmlResponse("https://example.com", body=body)
+
+ start_time = process_time()
+
+ open_in_browser(response, lambda url: True)
+
+ end_time = process_time()
+ self.assertLess(end_time - start_time, MAX_CPU_TIME)
+
+ def test_open_in_browser_redos_head(self):
+ MAX_CPU_TIME = 15
+
+ # Exploit input from
+ # https://makenowjust-labs.github.io/recheck/playground/
+ # for /(|\s.*?>))/ (old pattern to find the head element).
+ body = b"