Browse Source

fix: check if the browser supports Sec-Fetch headers (#4696)

Émilien (perso) 2 days ago
parent
commit
19b116f1d7
1 changed files with 56 additions and 14 deletions
  1. 56 14
      searx/botdetection/http_sec_fetch.py

+ 56 - 14
searx/botdetection/http_sec_fetch.py

@@ -28,6 +28,7 @@ from ipaddress import (
     IPv6Network,
 )
 
+import re
 import flask
 import werkzeug
 
@@ -37,25 +38,66 @@ from . import config
 from ._helpers import logger
 
 
+def is_browser_supported(user_agent: str) -> bool:
+    """Check if the browser supports Sec-Fetch headers.
+
+    https://caniuse.com/mdn-http_headers_sec-fetch-dest
+    https://caniuse.com/mdn-http_headers_sec-fetch-mode
+    https://caniuse.com/mdn-http_headers_sec-fetch-site
+
+    Supported browsers:
+    - Chrome >= 80
+    - Firefox >= 90
+    - Safari >= 16.4
+    - Edge (mirrors Chrome)
+    - Opera (mirrors Chrome)
+    """
+    user_agent = user_agent.lower()
+
+    # Chrome/Chromium/Edge/Opera
+    chrome_match = re.search(r'chrome/(\d+)', user_agent)
+    if chrome_match:
+        version = int(chrome_match.group(1))
+        return version >= 80
+
+    # Firefox
+    firefox_match = re.search(r'firefox/(\d+)', user_agent)
+    if firefox_match:
+        version = int(firefox_match.group(1))
+        return version >= 90
+
+    # Safari
+    safari_match = re.search(r'version/(\d+)\.(\d+)', user_agent)
+    if safari_match:
+        major = int(safari_match.group(1))
+        minor = int(safari_match.group(2))
+        return major > 16 or (major == 16 and minor >= 4)
+
+    return False
+
+
 def filter_request(
     network: IPv4Network | IPv6Network,
     request: SXNG_Request,
     cfg: config.Config,
 ) -> werkzeug.Response | None:
 
-    val = request.headers.get("Sec-Fetch-Mode", "")
-    if val != "navigate":
-        logger.debug("invalid Sec-Fetch-Mode '%s'", val)
-        return flask.redirect(flask.url_for('index'), code=302)
-
-    val = request.headers.get("Sec-Fetch-Site", "")
-    if val not in ('same-origin', 'same-site', 'none'):
-        logger.debug("invalid Sec-Fetch-Site '%s'", val)
-        flask.redirect(flask.url_for('index'), code=302)
-
-    val = request.headers.get("Sec-Fetch-Dest", "")
-    if val != "document":
-        logger.debug("invalid Sec-Fetch-Dest '%s'", val)
-        flask.redirect(flask.url_for('index'), code=302)
+    # Only check Sec-Fetch headers for supported browsers
+    user_agent = request.headers.get('User-Agent', '')
+    if is_browser_supported(user_agent):
+        val = request.headers.get("Sec-Fetch-Mode", "")
+        if val != "navigate":
+            logger.debug("invalid Sec-Fetch-Mode '%s'", val)
+            return flask.redirect(flask.url_for('index'), code=302)
+
+        val = request.headers.get("Sec-Fetch-Site", "")
+        if val not in ('same-origin', 'same-site', 'none'):
+            logger.debug("invalid Sec-Fetch-Site '%s'", val)
+            flask.redirect(flask.url_for('index'), code=302)
+
+        val = request.headers.get("Sec-Fetch-Dest", "")
+        if val != "document":
+            logger.debug("invalid Sec-Fetch-Dest '%s'", val)
+            flask.redirect(flask.url_for('index'), code=302)
 
     return None