diff --git a/app/filter.py b/app/filter.py index dda07d8..731b6aa 100644 --- a/app/filter.py +++ b/app/filter.py @@ -349,7 +349,12 @@ class Filter: if any(url in link['href'] for url in unsupported_g_pages): # FIXME: The "Shopping" tab requires further filtering (see #136) # Temporarily removing all links to that tab for now. - link.decompose() + parent = link.parent + while parent: + p_cls = parent.attrs.get('class') or [] + if parent.name == 'footer' or f'{GClasses.footer}' in p_cls: + link.decompose() + parent = parent.parent return # Replace href with only the intended destination (no "utm" type tags) diff --git a/app/models/g_classes.py b/app/models/g_classes.py index 2bd53a3..9f394ce 100644 --- a/app/models/g_classes.py +++ b/app/models/g_classes.py @@ -11,6 +11,7 @@ class GClasses(Enum): """ main_tbm_tab = 'KP7LCb' images_tbm_tab = 'n692Zd' + footer = 'TuS8Ad' def __str__(self): return self.value diff --git a/app/request.py b/app/request.py index 17ac034..d222c45 100644 --- a/app/request.py +++ b/app/request.py @@ -1,6 +1,6 @@ from app.models.config import Config from datetime import datetime -import xml.etree.ElementTree as ET +from defusedxml import ElementTree as ET import random import requests from requests import Response, ConnectionError diff --git a/app/routes.py b/app/routes.py index 350621f..f4308e1 100644 --- a/app/routes.py +++ b/app/routes.py @@ -304,8 +304,8 @@ def search(): search_util = Search(request, g.user_config, g.session_key) query = search_util.new_search_query() - bang = resolve_bang(query=query, bangs_dict=bang_json) - if bang != '': + bang = resolve_bang(query, bang_json, url_for('.index')) + if bang: return redirect(bang) # Redirect to home if invalid/blank search diff --git a/app/utils/bangs.py b/app/utils/bangs.py index 4a1a3cb..2650658 100644 --- a/app/utils/bangs.py +++ b/app/utils/bangs.py @@ -38,7 +38,7 @@ def gen_bangs_json(bangs_file: str) -> None: print('* Finished creating ddg bangs json') -def resolve_bang(query: str, bangs_dict: dict) -> str: +def resolve_bang(query: str, bangs_dict: dict, fallback: str) -> str: """Transform's a user's query to a bang search, if an operator is found Args: @@ -59,8 +59,14 @@ def resolve_bang(query: str, bangs_dict: dict) -> str: if operator not in split_query \ and operator[1:] + operator[0] not in split_query: continue - return bangs_dict[operator]['url'].replace( - '{}', - query.replace(operator if operator in split_query - else operator[1:] + operator[0], '').strip(), 1) + + bang_query = query.replace( + operator if operator in split_query else operator[1:] + + operator[0], '' + ).strip() + + if bang_query: + return bangs_dict[operator]['url'].replace('{}', bang_query, 1) + else: + return fallback return '' diff --git a/requirements.txt b/requirements.txt index 05a3872..d6fc75f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,7 @@ cffi==1.15.0 chardet==3.0.4 click==8.0.3 cryptography==3.3.2 +defusedxml==0.7.1 Flask==1.1.1 Flask-Session==0.4.0 idna==2.9 diff --git a/setup.cfg b/setup.cfg index 7edf18a..1d3c2f3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,6 +19,7 @@ include_package_data = True install_requires= beautifulsoup4 cryptography + defusedxml Flask Flask-Session python-dotenv diff --git a/test/test_routes.py b/test/test_routes.py index e71e995..817a555 100644 --- a/test/test_routes.py +++ b/test/test_routes.py @@ -47,6 +47,11 @@ def test_ddg_bang(client): assert rv._status_code == 302 assert rv.headers.get('Location').startswith('https://github.com') + # Ensure bang without content doesn't redirect to the result + rv = client.get(f'/{Endpoint.search}?q=!gh') + assert rv._status_code == 302 + assert not rv.headers.get('Location').startswith('https://github.com') + def test_config(client): rv = client.post(f'/{Endpoint.config}', data=demo_config)