From 269326063ac9113dacb222c3b1dd0516483e05b8 Mon Sep 17 00:00:00 2001
From: Alexandre Flament <alex@al-f.net>
Date: Sun, 4 Dec 2022 23:43:59 +0100
Subject: [PATCH] Fix: don't crash when engine or name is missing in
 settings.yml

SearXNG crashes if the engine or name fields are missing.
With this commit, the app displays an error in the log and keeps loading.

Close #1951
---
 searx/engines/__init__.py       | 11 +++++++++--
 tests/unit/test_engines_init.py | 22 ++++++++++++++++++++++
 2 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/searx/engines/__init__.py b/searx/engines/__init__.py
index c61f50d4b..52bb5f20d 100644
--- a/searx/engines/__init__.py
+++ b/searx/engines/__init__.py
@@ -106,8 +106,12 @@ def load_engine(engine_data: dict) -> Optional[Engine]:
     - required attribute is not set :py:func:`is_missing_required_attributes`
 
     """
+    # pylint: disable=too-many-return-statements
 
-    engine_name = engine_data['name']
+    engine_name = engine_data.get('name')
+    if engine_name is None:
+        logger.error('An engine does not have a "name" field')
+        return None
     if '_' in engine_name:
         logger.error('Engine name contains underscore: "{}"'.format(engine_name))
         return None
@@ -118,7 +122,10 @@ def load_engine(engine_data: dict) -> Optional[Engine]:
         engine_data['name'] = engine_name
 
     # load_module
-    engine_module = engine_data['engine']
+    engine_module = engine_data.get('engine')
+    if engine_module is None:
+        logger.error('The "engine" field is missing for the engine named "{}"'.format(engine_name))
+        return None
     try:
         engine = load_module(engine_module + '.py', ENGINE_DIR)
     except (SyntaxError, KeyboardInterrupt, SystemExit, SystemError, ImportError, RuntimeError):
diff --git a/tests/unit/test_engines_init.py b/tests/unit/test_engines_init.py
index c72f5c8e5..2844569d1 100644
--- a/tests/unit/test_engines_init.py
+++ b/tests/unit/test_engines_init.py
@@ -53,3 +53,25 @@ class TestEnginesInit(SearxTestCase):
         self.assertIn('onions', engines.categories)
         self.assertIn('http://engine1.onion', engines.engines['engine1'].search_url)
         self.assertEqual(engines.engines['engine1'].timeout, 120.0)
+
+    def test_missing_name_field(self):
+        settings['outgoing']['using_tor_proxy'] = False
+        engine_list = [
+            {'engine': 'dummy', 'shortcut': 'e1', 'categories': 'general'},
+        ]
+        with self.assertLogs('searx.engines', level='ERROR') as cm:
+            engines.load_engines(engine_list)
+            self.assertEqual(len(engines.engines), 0)
+            self.assertEqual(cm.output, ['ERROR:searx.engines:An engine does not have a "name" field'])
+
+    def test_missing_engine_field(self):
+        settings['outgoing']['using_tor_proxy'] = False
+        engine_list = [
+            {'name': 'engine2', 'shortcut': 'e2', 'categories': 'onions'},
+        ]
+        with self.assertLogs('searx.engines', level='ERROR') as cm:
+            engines.load_engines(engine_list)
+            self.assertEqual(len(engines.engines), 0)
+            self.assertEqual(
+                cm.output, ['ERROR:searx.engines:The "engine" field is missing for the engine named "engine2"']
+            )