Repository: google/timesketch

Issue:
Auto analyzer execution runs into an error
**Describe the bug**
AUTO_INDEX_ANALYZERS = ['tagger','login'] was set in timesketch.conf

**To Reproduce**
Upload a plaso file after adding the AUTO_INDEX_ANALYZERS into TS config.

Following error is visible in TS logs.

[2021-03-22 12:35:01,230] celery.worker.strategy/INFO Received task: timesketch.lib.tasks.run_index_analyzer[8e7bbf55-35db-4d84-884d-d7e696581c8b]
[2021-03-22 12:35:01,298] celery.app.trace/ERROR Task timesketch.lib.tasks.run_index_analyzer[8e7bbf55-35db-4d84-884d-d7e696581c8b] raised unexpected: TypeError("__init__() missing 1 required positional argument: 'sketch_id'")
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/celery/app/trace.py", line 385, in trace_task
    R = retval = fun(*args, **kwargs)
  File "/usr/local/lib/python3.8/dist-packages/timesketch/app.py", line 198, in __call__
    return TaskBase.__call__(self, *args, **kwargs)
  File "/usr/local/lib/python3.8/dist-packages/celery/app/trace.py", line 650, in __protected_call__
    return self.run(*args, **kwargs)
  File "/usr/local/lib/python3.8/dist-packages/timesketch/lib/tasks.py", line 481, in run_index_analyzer
    analyzer = analyzer_class(index_name=index_name, **kwargs)




Discussion:

Resolving patch:
diff --git a/timesketch/lib/analyzers/manager.py b/timesketch/lib/analyzers/manager.py
--- a/timesketch/lib/analyzers/manager.py
+++ b/timesketch/lib/analyzers/manager.py
@@ -78,11 +78,14 @@ def clear_registration(cls):
         cls._class_registry = {}
 
     @classmethod
-    def get_analyzers(cls, analyzer_names=None):
+    def get_analyzers(cls, analyzer_names=None, sketch_analyzers=True):
         """Retrieves the registered analyzers.
 
         Args:
             analyzer_names (list): List of analyzer names.
+            sketch_analyzers (bool): If True (default) then only sketch
+                analyzers are return, otherwise it will return Index
+                analyzers.
 
         Yields:
             tuple: containing:
@@ -99,7 +102,13 @@ def get_analyzers(cls, analyzer_names=None):
                 if analyzer_name in completed_analyzers:
                     continue
                 analyzer_class = cls.get_analyzer(analyzer_name)
-                yield analyzer_name, analyzer_class
+                if sketch_analyzers:
+                    if analyzer_class.IS_SKETCH_ANALYZER:
+                        yield analyzer_name, analyzer_class
+                else:
+                    if not analyzer_class.IS_SKETCH_ANALYZER:
+                        yield analyzer_name, analyzer_class
+
                 completed_analyzers.add(analyzer_name)
 
     @classmethod
diff --git a/timesketch/lib/datastores/elastic.py b/timesketch/lib/datastores/elastic.py
--- a/timesketch/lib/datastores/elastic.py
+++ b/timesketch/lib/datastores/elastic.py
@@ -117,18 +117,16 @@ def __init__(self, host='127.0.0.1', port=9200):
         self.ssl = current_app.config.get('ELASTIC_SSL', False)
         self.verify = current_app.config.get('ELASTIC_VERIFY_CERTS', True)
 
+        parameters = {}
         if self.ssl:
-            if self.user and self.password:
-                self.client = Elasticsearch(
-                    [{'host': host, 'port': port}],
-                    http_auth=(self.user, self.password),
-                    use_ssl=self.ssl, verify_certs=self.verify)
-            else:
-                self.client = Elasticsearch(
-                    [{'host': host, 'port': port}],
-                    use_ssl=self.ssl, verify_certs=self.verify)
-        else:
-            self.client = Elasticsearch([{'host': host, 'port': port}])
+            parameters['use_ssl'] = self.ssl
+            parameters['verify_certs'] = self.verify
+
+        if self.user and self.password:
+            parameters['http_auth'] = (self.user, self.password)
+
+        self.client = Elasticsearch(
+            [{'host': host, 'port': port}], **parameters)
 
         self.import_counter = Counter()
         self.import_events = []
diff --git a/timesketch/lib/tasks.py b/timesketch/lib/tasks.py
--- a/timesketch/lib/tasks.py
+++ b/timesketch/lib/tasks.py
@@ -220,13 +220,13 @@ def _get_index_analyzers():
         None if index analyzers are disabled in config.
     """
     tasks = []
-    index_analyzers = current_app.config.get('AUTO_INDEX_ANALYZERS')
+    index_analyzers = current_app.config.get('AUTO_INDEX_ANALYZERS', [])
 
     if not index_analyzers:
         return None
 
     for analyzer_name, _ in manager.AnalysisManager.get_analyzers(
-            index_analyzers):
+            index_analyzers, sketch_analyzers=False):
         tasks.append(run_index_analyzer.s(analyzer_name))
 
     return chain(tasks)


Test patch:
diff --git a/timesketch/lib/analyzers/manager_test.py b/timesketch/lib/analyzers/manager_test.py
--- a/timesketch/lib/analyzers/manager_test.py
+++ b/timesketch/lib/analyzers/manager_test.py
@@ -24,6 +24,7 @@ class MockAnalyzer(object):
     NAME = 'MockAnalyzer'
 
     DEPENDENCIES = frozenset()
+    IS_SKETCH_ANALYZER = True
 
 
 class MockAnalyzer2(object):
@@ -31,6 +32,15 @@ class MockAnalyzer2(object):
     NAME = 'MockAnalyzer2'
 
     DEPENDENCIES = frozenset(['MockAnalyzer'])
+    IS_SKETCH_ANALYZER = True
+
+
+class MockIndexAnalyzer(object):
+    """Mock analyzer class,"""
+    NAME = 'MockIndexAnalyzer'
+
+    DEPENDENCIES = frozenset()
+    IS_SKETCH_ANALYZER = False
 
 
 class MockAnalyzer3(object):
@@ -38,6 +48,7 @@ class MockAnalyzer3(object):
     NAME = 'MockAnalyzer3'
 
     DEPENDENCIES = frozenset()
+    IS_SKETCH_ANALYZER = True
 
 
 class MockAnalyzer4(object):
@@ -45,6 +56,7 @@ class MockAnalyzer4(object):
     NAME = 'MockAnalyzer4'
 
     DEPENDENCIES = frozenset(['MockAnalyzer2', 'MockAnalyzer3'])
+    IS_SKETCH_ANALYZER = True
 
 
 class MockAnalyzerFail1(object):
@@ -52,6 +64,7 @@ class MockAnalyzerFail1(object):
     NAME = 'MockAnalyzerFail1'
 
     DEPENDENCIES = frozenset(['MockAnalyzerFail2'])
+    IS_SKETCH_ANALYZER = True
 
 
 class MockAnalyzerFail2(object):
@@ -59,6 +72,7 @@ class MockAnalyzerFail2(object):
     NAME = 'MockAnalyzerFail2'
 
     DEPENDENCIES = frozenset(['MockAnalyzerFail1'])
+    IS_SKETCH_ANALYZER = True
 
 
 class TestAnalysisManager(BaseTest):
@@ -66,7 +80,7 @@ class TestAnalysisManager(BaseTest):
 
     def setUp(self):
         """Set up the tests."""
-        super(TestAnalysisManager, self).setUp()
+        super().setUp()
         manager.AnalysisManager.clear_registration()
         manager.AnalysisManager.register_analyzer(MockAnalyzer)
 
@@ -91,6 +105,7 @@ def test_get_analyzers(self):
         manager.AnalysisManager.register_analyzer(MockAnalyzer2)
         manager.AnalysisManager.register_analyzer(MockAnalyzer3)
         manager.AnalysisManager.register_analyzer(MockAnalyzer4)
+        manager.AnalysisManager.register_analyzer(MockIndexAnalyzer)
 
         analyzers = manager.AnalysisManager.get_analyzers()
         analyzer_names_list = [x for x, _ in analyzers]
@@ -112,6 +127,11 @@ def test_get_analyzers(self):
         self.assertIn('mockanalyzer2', dependency_tree[1])
         self.assertIn('mockanalyzer4', dependency_tree[2])
 
+        analyzers = [x for x, _ in manager.AnalysisManager.get_analyzers(
+            sketch_analyzers=False)]
+        self.assertEqual(len(analyzers), 1)
+        self.assertEqual(analyzers[0], 'mockindexanalyzer')
+
         manager.AnalysisManager.clear_registration()
         manager.AnalysisManager.register_analyzer(MockAnalyzerFail1)
         manager.AnalysisManager.register_analyzer(MockAnalyzerFail2)