Repository: ShaneKent/PyEventLogViewer

Issue:
Unit test to check that parsing errors are handled
Originally from issue #89, failed during that sprint.

Add a unit test which passes invalid data to the parser and tests that errors are handled.

Comments:

Patch:
diff --git a/winlogtimeline/collector/parser.py b/winlogtimeline/collector/parser.py
index 8ebc740..72276ff 100644
--- a/winlogtimeline/collector/parser.py
+++ b/winlogtimeline/collector/parser.py
@@ -379,6 +379,10 @@ def parser(raw, record):
     #     if user_parsers.get(record['event_source'], {}).get(record['event_id'], None) is not None:
     #         parse_record = user_parser
 
+    if get_event_data_section(raw) is False:
+        print('NOTE: A corrupted record was almost parsed with EventID ', record['event_id'])
+        return None
+
     if parse_record is not None:
         try:
             record = parse_record(raw, record)
@@ -400,3 +404,17 @@ def get_string(string):
     if isinstance(string, str):
         return string
     return string["#text"]
+
+
+def get_event_data_section(raw):
+    """
+    Helper to get the 'EventData' or 'UserData' key of the base XML for each record. If this fails, it is very likely that the
+    record is corrupted due to the log recovery software.
+    :param raw:
+    :return: whether or not the key exists.
+    """
+
+    if 'EventData' not in raw['Event'] and 'UserData' not in raw['Event']:
+        return False
+
+    return True
diff --git a/winlogtimeline/ui/ui.py b/winlogtimeline/ui/ui.py
index ab367b2..7f0c94b 100644
--- a/winlogtimeline/ui/ui.py
+++ b/winlogtimeline/ui/ui.py
@@ -52,22 +52,11 @@ class GUI(Tk):
         self.system = platform.system()
         self.changes_made = False
 
-        self.try_create_startup_window()
+        self.create_startup_window()
 
         self.__disable__()
         self.protocol('WM_DELETE_WINDOW', self.__destroy__)
 
-    def try_create_startup_window(self):
-        """
-        Checks the config to determine whether or not a start-up window should be created.
-        :return:
-        """
-        if self.program_config.get("startup_window", None):
-            window = StartupWindow(self)
-            window.attributes('-topmost', True)
-        else:
-            print("NOTE: Please delete your local 'config.json' file.")
-
     def update_status_bar(self, text):
         """
         Updates the status bar.
@@ -213,6 +202,25 @@ class GUI(Tk):
         t = Thread(target=callback)
         t.start()
 
+    def create_startup_window(self):
+        """
+        Helper function that checks the config.json file and decides whether or not to display the startup window.
+        :return:
+        """
+        startup_window = self.program_config.get("startup_window", None)
+
+        # Check to see if the config.json file has a startup_window key.
+        if startup_window is None:
+            # self.program_config.update({"startup_window": True})
+            self.program_config["startup_window"] = True
+            startup_window = True
+
+            util.data.write_config(self.program_config)
+
+        if startup_window:
+            window = StartupWindow(self)
+            window.attributes('-topmost', True)
+
     def __disable__(self):
         self.enabled = False
         if self.system != 'Darwin':


Test patch:
diff --git a/tests/test_parser.py b/tests/test_parser.py
index 25cdbe1..f05251c 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -1177,6 +1177,49 @@ def test_parse_wanted():
            }
     assert parser.parser(d, rec) is not None
 
+
+def test_corrupt_record():
+    xml_corrupt = (
+        '<Event xmlns = "http://schemas.microsoft.com/win/2004/08/events/event">'
+        '<System>'
+        '<Provider Name="Microsoft-Windows-Power-Troubleshooter" Guid="{A68CA8B7-004F-D7B6-A698-07E2DE0F1F5D}"/>'
+        '<EventID>1</EventID>'
+        '<Version>1</Version>'
+        '<Level>4</Level>'
+        '<Task>5</Task>'
+        '<Opcode>0</Opcode>'
+        '<Keywords>0x8000000000000010</Keywords>'
+        '<TimeCreated SystemTime="2017-01-02T00:28:02.499911900Z"/>'
+        '<EventRecordID>521</EventRecordID>'
+        '<Correlation/>'
+        '<Execution ProcessID="4" ThreadID="372"/>'
+        '<Channel>System</Channel>'
+        '<Computer>LAPTOP-9KUQNI2Q</Computer>'
+        '<Security/>'
+        '</System>'
+        'CORRUPTED'
+        'LIGHTNING STRIKES THE WIRE'
+        'ZZZZZAAAAAAAPPPPPPP!!!!!'
+        '</Event>'
+    )
+    d = xmltodict.parse(xml_corrupt)
+    sys = d['Event']['System']
+    rec = {'timestamp_utc': sys['TimeCreated']['@SystemTime'],
+           'event_id': sys['EventID'],
+           'description': '',
+           'details': '',
+           'event_source': sys['Provider']['@Name'],
+           'event_log': sys['Channel'],
+           'session_id': '',
+           'account': '',
+           'computer_name': sys['Computer'],
+           'record_number': sys['EventRecordID'],
+           'recovered': True,
+           'alias': 'Sample'
+           }
+
+    assert parser.parser(d, rec) is None
+
 # def test_parser():
 #     for record in records:
 #         try: