mirror of
https://github.com/searxng/searxng.git
synced 2026-09-11 16:56:05 +00:00
[fix] engines: startpage anubis solver
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
"""Startpage's language & region selectors are a mess ..
|
"""Startpage requires solving an Anubis POW captcha (difficulty 4).
|
||||||
|
Solving it requires a lot of CPU, so the engine is set inactive by default.
|
||||||
|
|
||||||
|
Startpage's language & region selectors are a mess ..
|
||||||
|
|
||||||
.. _startpage regions:
|
.. _startpage regions:
|
||||||
|
|
||||||
@@ -84,6 +87,7 @@ Startpage's category (for Web-search, News, Videos, ..) is set by
|
|||||||
"""
|
"""
|
||||||
# pylint: disable=too-many-statements
|
# pylint: disable=too-many-statements
|
||||||
|
|
||||||
|
import hashlib
|
||||||
import re
|
import re
|
||||||
import typing as t
|
import typing as t
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
@@ -99,7 +103,7 @@ from searx.enginelib import EngineCache
|
|||||||
from searx.enginelib.traits import EngineTraits
|
from searx.enginelib.traits import EngineTraits
|
||||||
from searx.exceptions import SearxEngineCaptchaException
|
from searx.exceptions import SearxEngineCaptchaException
|
||||||
from searx.locales import region_tag
|
from searx.locales import region_tag
|
||||||
from searx.network import get # see https://github.com/searxng/searxng/issues/762
|
from searx.network import get, post # see https://github.com/searxng/searxng/issues/762
|
||||||
from searx.utils import (
|
from searx.utils import (
|
||||||
eval_xpath,
|
eval_xpath,
|
||||||
extr,
|
extr,
|
||||||
@@ -176,6 +180,45 @@ def setup(_: dict[str, t.Any]) -> bool | None:
|
|||||||
sc_code_cache_sec = 3600
|
sc_code_cache_sec = 3600
|
||||||
"""Time in seconds the sc-code is cached in memory :py:obj:`get_sc_code`."""
|
"""Time in seconds the sc-code is cached in memory :py:obj:`get_sc_code`."""
|
||||||
|
|
||||||
|
# startpage's anubis difficulty is set to 4
|
||||||
|
max_difficulty = 4
|
||||||
|
|
||||||
|
|
||||||
|
def _solve_anubis(resp) -> str:
|
||||||
|
"""Anubis POW solver"""
|
||||||
|
payload = loads(extr(resp.text, '<script id="anubis_challenge" type="application/json">', "</script>"))
|
||||||
|
challenge = payload["challenge"]
|
||||||
|
difficulty = int(payload["rules"]["difficulty"])
|
||||||
|
if difficulty > max_difficulty:
|
||||||
|
raise SearxEngineCaptchaException(message="startpage: Anubis difficulty too high")
|
||||||
|
prefix = "0" * difficulty
|
||||||
|
blob = challenge["randomData"].encode()
|
||||||
|
for nonce in range(16**difficulty * 8): # max search is 8x average search, e^-8 = 0.034% will fail
|
||||||
|
digest = hashlib.sha256(blob + str(nonce).encode()).hexdigest()
|
||||||
|
if digest.startswith(prefix):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise SearxEngineCaptchaException(message="startpage: Anubis failed")
|
||||||
|
|
||||||
|
pass_resp = get(
|
||||||
|
f"{base_url}/.within.website/x/cmd/anubis/api/pass-challenge",
|
||||||
|
params={
|
||||||
|
"id": challenge["id"],
|
||||||
|
"response": digest,
|
||||||
|
"nonce": nonce,
|
||||||
|
"redir": str(resp.url),
|
||||||
|
"elapsedTime": "1",
|
||||||
|
},
|
||||||
|
cookies=resp.cookies,
|
||||||
|
allow_redirects=False,
|
||||||
|
)
|
||||||
|
auth = pass_resp.cookies.get("spchal-auth")
|
||||||
|
if not auth:
|
||||||
|
raise SearxEngineCaptchaException(message="startpage: Anubis pass-challenge failed")
|
||||||
|
auth = str(auth)
|
||||||
|
CACHE.set("SPCHAL_AUTH", auth, expire=240)
|
||||||
|
return auth
|
||||||
|
|
||||||
|
|
||||||
def get_sc_code(params):
|
def get_sc_code(params):
|
||||||
"""Get an actual ``sc`` argument from Startpage's search form (HTML page).
|
"""Get an actual ``sc`` argument from Startpage's search form (HTML page).
|
||||||
@@ -201,6 +244,9 @@ def get_sc_code(params):
|
|||||||
logger.debug("get_sc_code: request headers: %s", headers)
|
logger.debug("get_sc_code: request headers: %s", headers)
|
||||||
resp = get(get_sc_url, headers=headers)
|
resp = get(get_sc_url, headers=headers)
|
||||||
|
|
||||||
|
if 'id="anubis_challenge"' in resp.text:
|
||||||
|
resp = get(get_sc_url, headers=headers, cookies={"spchal-auth": _solve_anubis(resp)})
|
||||||
|
|
||||||
# ?? x = network.get('https://www.startpage.com/sp/cdn/images/filter-chevron.svg', headers=headers)
|
# ?? x = network.get('https://www.startpage.com/sp/cdn/images/filter-chevron.svg', headers=headers)
|
||||||
# ?? https://www.startpage.com/sp/cdn/images/filter-chevron.svg
|
# ?? https://www.startpage.com/sp/cdn/images/filter-chevron.svg
|
||||||
# ?? ping-back URL: https://www.startpage.com/sp/pb?sc=TLsB0oITjZ8F21
|
# ?? ping-back URL: https://www.startpage.com/sp/pb?sc=TLsB0oITjZ8F21
|
||||||
@@ -239,8 +285,8 @@ def request(query, params):
|
|||||||
Additionally the arguments form Startpage's search form needs to be set in
|
Additionally the arguments form Startpage's search form needs to be set in
|
||||||
HTML POST data / compare ``<input>`` elements: :py:obj:`search_form_xpath`.
|
HTML POST data / compare ``<input>`` elements: :py:obj:`search_form_xpath`.
|
||||||
"""
|
"""
|
||||||
engine_region = traits.get_region(params["searxng_locale"], "en-US")
|
engine_region = traits.get_region(params["searxng_locale"], "en_US")
|
||||||
engine_language = traits.get_language(params["searxng_locale"], "en")
|
engine_language = traits.get_language(params["searxng_locale"], "english")
|
||||||
|
|
||||||
params["headers"]["Origin"] = base_url
|
params["headers"]["Origin"] = base_url
|
||||||
params["headers"]["Referer"] = base_url + "/"
|
params["headers"]["Referer"] = base_url + "/"
|
||||||
@@ -262,9 +308,9 @@ def request(query, params):
|
|||||||
args["language"] = engine_language
|
args["language"] = engine_language
|
||||||
args["lui"] = engine_language
|
args["lui"] = engine_language
|
||||||
|
|
||||||
|
args["segment"] = "startpage.udog"
|
||||||
if params["pageno"] > 1:
|
if params["pageno"] > 1:
|
||||||
args["page"] = params["pageno"]
|
args["page"] = params["pageno"]
|
||||||
args["segment"] = "startpage.udog"
|
|
||||||
|
|
||||||
# Build cookie
|
# Build cookie
|
||||||
lang_homepage = "en"
|
lang_homepage = "en"
|
||||||
@@ -289,6 +335,8 @@ def request(query, params):
|
|||||||
cookie["search_results_region"] = engine_region
|
cookie["search_results_region"] = engine_region
|
||||||
|
|
||||||
params["cookies"]["preferences"] = "N1N".join(["%sEEE%s" % x for x in cookie.items()])
|
params["cookies"]["preferences"] = "N1N".join(["%sEEE%s" % x for x in cookie.items()])
|
||||||
|
if auth := CACHE.get("SPCHAL_AUTH"):
|
||||||
|
params["cookies"]["spchal-auth"] = auth
|
||||||
logger.debug("cookie preferences: %s", params["cookies"]["preferences"])
|
logger.debug("cookie preferences: %s", params["cookies"]["preferences"])
|
||||||
|
|
||||||
logger.debug("data: %s", args)
|
logger.debug("data: %s", args)
|
||||||
@@ -400,6 +448,18 @@ def _get_image_result(result) -> dict[str, t.Any] | None:
|
|||||||
|
|
||||||
|
|
||||||
def response(resp):
|
def response(resp):
|
||||||
|
if 'id="anubis_challenge"' in resp.text:
|
||||||
|
params = resp.search_params
|
||||||
|
params["cookies"]["spchal-auth"] = _solve_anubis(resp)
|
||||||
|
resp = post(
|
||||||
|
params["url"] or search_url,
|
||||||
|
data=params["data"],
|
||||||
|
headers=params["headers"],
|
||||||
|
cookies=params["cookies"],
|
||||||
|
)
|
||||||
|
if 'id="anubis_challenge"' in resp.text:
|
||||||
|
raise SearxEngineCaptchaException()
|
||||||
|
|
||||||
categ = startpage_categ.capitalize()
|
categ = startpage_categ.capitalize()
|
||||||
results_raw = "{" + extr(resp.text, f"React.createElement(UIStartpage.AppSerp{categ}, {{", "}})") + "}}"
|
results_raw = "{" + extr(resp.text, f"React.createElement(UIStartpage.AppSerp{categ}, {{", "}})") + "}}"
|
||||||
|
|
||||||
|
|||||||
@@ -2343,18 +2343,21 @@ engines:
|
|||||||
shortcut: sp
|
shortcut: sp
|
||||||
startpage_categ: web
|
startpage_categ: web
|
||||||
categories: [general, web]
|
categories: [general, web]
|
||||||
|
inactive: true # uses a Proof Of Work captcha https://github.com/searxng/searxng/pull/6669
|
||||||
|
|
||||||
- name: startpage news
|
- name: startpage news
|
||||||
engine: startpage
|
engine: startpage
|
||||||
startpage_categ: news
|
startpage_categ: news
|
||||||
categories: [news, web]
|
categories: [news, web]
|
||||||
shortcut: spn
|
shortcut: spn
|
||||||
|
inactive: true # uses a Proof Of Work captcha https://github.com/searxng/searxng/pull/6669
|
||||||
|
|
||||||
- name: startpage images
|
- name: startpage images
|
||||||
engine: startpage
|
engine: startpage
|
||||||
startpage_categ: images
|
startpage_categ: images
|
||||||
categories: [images, web]
|
categories: [images, web]
|
||||||
shortcut: spi
|
shortcut: spi
|
||||||
|
inactive: true # uses a Proof Of Work captcha https://github.com/searxng/searxng/pull/6669
|
||||||
|
|
||||||
- name: steam
|
- name: steam
|
||||||
engine: steam
|
engine: steam
|
||||||
|
|||||||
Reference in New Issue
Block a user