Repository: log2timeline/dftimewolf

Issue:
Check that log2timeline.py executable is present on system
No check is currently done.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/log2timeline/dftimewolf/pull/331?src=pr&el=h1) Report
> Merging [#331](https://codecov.io/gh/log2timeline/dftimewolf/pull/331?src=pr&el=desc) into [master](https://codecov.io/gh/log2timeline/dftimewolf/commit/550898e52be4c67ce27ba604843b2c5513a59bf1?el=desc) will **increase** coverage by `0.13%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/log2timeline/dftimewolf/pull/331/graphs/tree.svg?width=650&height=150&src=pr&token=aNZ3Zz7fzL)](https://codecov.io/gh/log2timeline/dftimewolf/pull/331?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #331      +/-   ##
==========================================
+ Coverage   71.40%   71.53%   +0.13%     
==========================================
  Files          30       30              
  Lines        2175     2185      +10     
==========================================
+ Hits         1553     1563      +10     
  Misses        622      622              
```


| [Impacted Files](https://codecov.io/gh/log2timeline/dftimewolf/pull/331?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [dftimewolf/lib/processors/localplaso.py](https://codecov.io/gh/log2timeline/dftimewolf/pull/331/diff?src=pr&el=tree#diff-ZGZ0aW1ld29sZi9saWIvcHJvY2Vzc29ycy9sb2NhbHBsYXNvLnB5) | `90.74% <100.00%> (+2.10%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/log2timeline/dftimewolf/pull/331?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/log2timeline/dftimewolf/pull/331?src=pr&el=footer). Last update [550898e...cc48f5d](https://codecov.io/gh/log2timeline/dftimewolf/pull/331?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/dftimewolf/lib/processors/localplaso.py b/dftimewolf/lib/processors/localplaso.py
index 61c3072..0e36dae 100644
--- a/dftimewolf/lib/processors/localplaso.py
+++ b/dftimewolf/lib/processors/localplaso.py
@@ -21,6 +21,16 @@ class LocalPlasoProcessor(module.BaseModule):
     super(LocalPlasoProcessor, self).__init__(state)
     self._timezone = None
     self._output_path = None
+    self._plaso_path = None
+
+  def _DeterminePlasoPath(self):
+    """Checks if log2timeline is somewhere in the user's PATH."""
+    for path in os.environ['PATH'].split(os.pathsep):
+      full_path = os.path.join(path, 'log2timeline.py')
+      if os.path.isfile(full_path):
+        self._plaso_path = full_path
+        return True
+    return False
 
   def SetUp(self, timezone=None):  # pylint: disable=arguments-differ
     """Sets up the local time zone with Plaso (log2timeline) should use.
@@ -30,6 +40,11 @@ class LocalPlasoProcessor(module.BaseModule):
     """
     self._timezone = timezone
     self._output_path = tempfile.mkdtemp()
+    if not self._DeterminePlasoPath():
+      self.ModuleError('log2timeline.py was not found in your PATH. To fix: \n'
+                       '  apt install plaso-tools',
+                       critical=True)
+
 
   def Process(self):
     """Executes log2timeline.py on the module input."""
@@ -40,7 +55,7 @@ class LocalPlasoProcessor(module.BaseModule):
       self.logger.info('Log file: {0:s}'.format(log_file_path))
 
       # Build the plaso command line.
-      cmd = ['log2timeline.py']
+      cmd = [self._plaso_path]
       # Since we might be running alongside another Module, always disable
       # the status view.
       cmd.extend(['-q', '--status_view', 'none'])


Test patch:
diff --git a/tests/lib/processors/localplaso.py b/tests/lib/processors/localplaso.py
index bd07ba8..f1d8a92 100644
--- a/tests/lib/processors/localplaso.py
+++ b/tests/lib/processors/localplaso.py
@@ -6,6 +6,7 @@ import unittest
 import mock
 
 from dftimewolf.lib import state
+from dftimewolf.lib import errors
 from dftimewolf.lib.processors import localplaso
 from dftimewolf.lib.containers import containers
 
@@ -22,14 +23,16 @@ class LocalPlasoTest(unittest.TestCase):
     self.assertIsNotNone(local_plaso_processor)
 
   # pylint: disable=invalid-name
+  @mock.patch('os.path.isfile')
   @mock.patch('subprocess.Popen')
-  def testProcessing(self, mock_Popen):
+  def testProcessing(self, mock_Popen, mock_exists):
     """Tests that the correct number of containers is added."""
     test_state = state.DFTimewolfState(config.Config)
     mock_popen_object = mock.Mock()
     mock_popen_object.communicate.return_value = (None, None)
     mock_popen_object.wait.return_value = False
     mock_Popen.return_value = mock_popen_object
+    mock_exists.return_value = True
 
     local_plaso_processor = localplaso.LocalPlasoProcessor(test_state)
     test_state.StoreContainer(
@@ -44,5 +47,31 @@ class LocalPlasoTest(unittest.TestCase):
         test_state.GetContainers(containers.File)[0].path,
         plaso_path)
 
+  @mock.patch.dict('os.environ', {'PATH': '/fake/path:/fake/path/2'})
+  @mock.patch('os.path.isfile')
+  def testPlasoCheck(self, mock_exists):
+    """Tests that a plaso executable is correctly located."""
+    test_state = state.DFTimewolfState(config.Config)
+    mock_exists.return_value = True
+    local_plaso_processor = localplaso.LocalPlasoProcessor(test_state)
+    # We're testing module internals here.
+    # pylint: disable=protected-access
+    local_plaso_processor._DeterminePlasoPath()
+    self.assertEqual(
+        local_plaso_processor._plaso_path, '/fake/path/log2timeline.py')
+
+  @mock.patch('os.path.isfile')
+  def testPlasoCheckFail(self, mock_exists):
+    """Tests that SetUp fails when no plaso executable is found."""
+    test_state = state.DFTimewolfState(config.Config)
+    mock_exists.return_value = False
+    local_plaso_processor = localplaso.LocalPlasoProcessor(test_state)
+    with self.assertRaises(errors.DFTimewolfError) as error:
+      local_plaso_processor.SetUp()
+    self.assertEqual(
+      ('log2timeline.py was not found in your PATH. To fix: \n'
+       '  apt install plaso-tools'),
+      error.exception.message)
+
 if __name__ == '__main__':
   unittest.main()