From 19b116f1d7aaea2ab2b00868003e9dd852eabbe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milien=20=28perso=29?= <4016501+unixfox@users.noreply.github.com> Date: Sun, 4 May 2025 08:12:25 +0000 Subject: [PATCH] fix: check if the browser supports Sec-Fetch headers (#4696) --- searx/botdetection/http_sec_fetch.py | 66 +++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/searx/botdetection/http_sec_fetch.py b/searx/botdetection/http_sec_fetch.py index 9791e74e1..536fbbd3f 100644 --- a/searx/botdetection/http_sec_fetch.py +++ b/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) + # 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-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) + 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