Source code for plaso.filters.dynamic_filter

# -*- coding: utf-8 -*-
"""The dynamic event object filter."""

from __future__ import unicode_literals

from plaso.filters import event_filter
from plaso.filters import manager
from plaso.lib import errors
from plaso.lib import lexer


[docs]class SelectiveLexer(lexer.Lexer): """Selective filter lexer implementation. The selective (or dynamic) filter allow to construct filter expressions like: SELECT field_a, field_b WHERE attribute contains 'text' """ tokens = [ lexer.Token('INITIAL', r'SELECT', '', 'FIELDS'), lexer.Token('FIELDS', r'(.+) WHERE ', 'SetFields', 'FILTER'), lexer.Token('FIELDS', r'(.+) LIMIT', 'SetFields', 'LIMIT_END'), lexer.Token('FIELDS', r'(.+) SEPARATED BY', 'SetFields', 'SEPARATE'), lexer.Token('FIELDS', r'(.+)$', 'SetFields', 'END'), lexer.Token('FILTER', r'(.+) SEPARATED BY', 'SetFilter', 'SEPARATE'), lexer.Token('FILTER', r'(.+) LIMIT', 'SetFilter', 'LIMIT_END'), lexer.Token('FILTER', r'(.+)$', 'SetFilter', 'END'), lexer.Token('SEPARATE', r' ', '', ''), # Ignore white space here. lexer.Token('SEPARATE', r'LIMIT', '', 'LIMIT_END'), lexer.Token( 'SEPARATE', r'[\'"]([^ \'"]+)[\'"] LIMIT', 'SetSeparator', 'LIMIT_END'), lexer.Token( 'SEPARATE', r'[\'"]([^ \'"]+)[\'"]$', 'SetSeparator', 'END'), lexer.Token( 'SEPARATE', r'(.+)$', 'SetSeparator', 'END'), lexer.Token( 'LIMIT_END', r'SEPARATED BY [\'"]([^\'"]+)[\'"]', 'SetSeparator', ''), lexer.Token('LIMIT_END', r'(.+) SEPARATED BY', 'SetLimit', 'SEPARATE'), lexer.Token('LIMIT_END', r'(.+)$', 'SetLimit', 'END')] def __init__(self, data=''): """Initializes a selective lexer. Args: data (str): optional initial data to be processed by the lexer. """ super(SelectiveLexer, self).__init__(data=data) self.fields = [] self.limit = 0 self.lex_filter = None self.separator = ',' # pylint 1.9.3 wants a docstring for kwargs, but this is not useful to add. # pylint: disable=missing-param-doc
[docs] def SetFields(self, match, **unused_kwargs): """Sets the output fields. The output fields is the part of the filter expression directly following the SELECT statement. Args: match (re.MatchObject): a match that contains the output field names. """ text = match.group(1).lower() field_text, _, _ = text.partition(' from ') use_field_text = field_text.replace(' ', '') if ',' in use_field_text: self.fields = use_field_text.split(',') else:
self.fields = [use_field_text] # pylint 1.9.3 wants a docstring for kwargs, but this is not useful to add. # pylint: disable=missing-param-doc
[docs] def SetFilter(self, match, **unused_kwargs): """Set the filter query. The filter query is the part of the filter expression directly following the WHERE statement. Args: match (re.MatchObject): a match that contains the filter query. """ filter_match = match.group(1) if 'LIMIT' in filter_match: # This only occurs in the case where we have "LIMIT X SEPARATED BY". self.lex_filter, _, push_back = filter_match.rpartition('LIMIT') self.PushBack('LIMIT {0:s} SEPARATED BY '.format(push_back)) else:
self.lex_filter = filter_match # pylint 1.9.3 wants a docstring for kwargs, but this is not useful to add. # pylint: disable=missing-param-doc
[docs] def SetLimit(self, match, **unused_kwargs): """Sets the row limit. Args: match (re.MatchObject): a match that contains the row limit. """ try: limit = int(match.group(1)) except ValueError: self.Error('Invalid limit value, should be int [{}] = {}'.format( type(match.group(1)), match.group(1))) limit = 0
self.limit = limit # pylint 1.9.3 wants a docstring for kwargs, but this is not useful to add. # pylint: disable=missing-param-doc
[docs] def SetSeparator(self, match, **unused_kwargs): """Sets the output field separator. Args: match (re.MatchObject): a match contains the output field separate. Note that only the first character is used. """ separator = match.group(1) if separator:
self.separator = separator[0]
[docs]class DynamicFilter(event_filter.EventObjectFilter): """Event filter that supports selective output fields. This filter is essentially the same as the event object filter except it wraps it in a selection of which fields should be included by an output module that supports selective fields, e.g. SELECT field_a, field_b WHERE attribute contains 'text' Will use the event object filter "attribute contains 'text'" and at the same time indicate to the appropriate output module that the user wants only the fields field_a and field_b to be used in the output. """ _STATE_END = 'END' def __init__(self): """Initializes a dynamic filter.""" super(DynamicFilter, self).__init__() self._fields = [] self._limit = 0 self._separator = ',' @property def fields(self): """list[str]: output fields.""" return self._fields @property def limit(self): """int: row limit.""" return self._limit @property def separator(self): """str: output field separator.""" return self._separator
[docs] def CompileFilter(self, filter_expression): """Compiles the filter expression. The filter expression contains an object filter expression extended with selective field selection. Args: filter_expression (str): filter expression. Raises: WrongPlugin: if the filter could not be compiled. """ lexer_object = SelectiveLexer(filter_expression) lexer_object.NextToken() if lexer_object.error: raise errors.WrongPlugin('Malformed filter string.') lexer_object.NextToken() if lexer_object.error: raise errors.WrongPlugin('No fields defined.') while lexer_object.state != self._STATE_END: lexer_object.NextToken() if lexer_object.error: raise errors.WrongPlugin('No filter defined for DynamicFilter.') if lexer_object.state != self._STATE_END: raise errors.WrongPlugin( 'Malformed DynamicFilter, end state not reached.') self._fields = lexer_object.fields self._limit = lexer_object.limit self._separator = '{0:s}'.format(lexer_object.separator) if lexer_object.lex_filter: super(DynamicFilter, self).CompileFilter(lexer_object.lex_filter) else: self._matcher = None
self._filter_expression = filter_expression manager.FiltersManager.RegisterFilter(DynamicFilter)