|
@@ -28,6 +28,7 @@ from ipaddress import (
|
|
IPv6Network,
|
|
IPv6Network,
|
|
)
|
|
)
|
|
|
|
|
|
|
|
+import re
|
|
import flask
|
|
import flask
|
|
import werkzeug
|
|
import werkzeug
|
|
|
|
|
|
@@ -37,25 +38,66 @@ from . import config
|
|
from ._helpers import logger
|
|
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(
|
|
def filter_request(
|
|
network: IPv4Network | IPv6Network,
|
|
network: IPv4Network | IPv6Network,
|
|
request: SXNG_Request,
|
|
request: SXNG_Request,
|
|
cfg: config.Config,
|
|
cfg: config.Config,
|
|
) -> werkzeug.Response | None:
|
|
) -> 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
|
|
return None
|