Import from old repository
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2013 The Plaso Project Authors.
|
||||
# Please see the AUTHORS file for details on individual authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""This file contains an import statement for each browser cookie plugin."""
|
||||
|
||||
from plaso.parsers.cookie_plugins import ganalytics
|
||||
@@ -0,0 +1,221 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2013 The Plaso Project Authors.
|
||||
# Please see the AUTHORS file for details on individual authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""This file contains a plugin for parsing Google Analytics cookies."""
|
||||
|
||||
import urllib
|
||||
|
||||
from plaso.events import time_events
|
||||
from plaso.lib import errors
|
||||
from plaso.lib import eventdata
|
||||
from plaso.parsers.cookie_plugins import interface
|
||||
|
||||
|
||||
class GoogleAnalyticsEvent(time_events.PosixTimeEvent):
|
||||
"""A simple placeholder for a Google Analytics event."""
|
||||
|
||||
DATA_TYPE = u'cookie:google:analytics'
|
||||
|
||||
def __init__(
|
||||
self, timestamp, timestamp_desc, url, data_type_append, cookie_name,
|
||||
**kwargs):
|
||||
"""Initialize a Google Analytics event.
|
||||
|
||||
Args:
|
||||
timestamp: The timestamp in a POSIX format.
|
||||
timestamp_desc: A string describing the timestamp.
|
||||
url: The full URL where the cookie got set.
|
||||
data_type_append: String to append to the data type.
|
||||
cookie_name: The name of the cookie.
|
||||
"""
|
||||
super(GoogleAnalyticsEvent, self).__init__(
|
||||
timestamp, timestamp_desc, u'{0:s}:{1:s}'.format(
|
||||
self.DATA_TYPE, data_type_append))
|
||||
|
||||
self.url = url
|
||||
self.cookie_name = cookie_name
|
||||
|
||||
for key, value in kwargs.iteritems():
|
||||
setattr(self, key, value)
|
||||
|
||||
|
||||
class GoogleAnalyticsUtmzPlugin(interface.CookiePlugin):
|
||||
"""A browser cookie plugin for Google Analytics cookies."""
|
||||
|
||||
NAME = 'google_analytics_utmz'
|
||||
|
||||
COOKIE_NAME = u'__utmz'
|
||||
|
||||
# Point to few sources for URL information.
|
||||
URLS = [
|
||||
(u'http://www.dfinews.com/articles/2012/02/'
|
||||
u'google-analytics-cookies-and-forensic-implications')]
|
||||
|
||||
def GetEntries(
|
||||
self, parser_context, file_entry=None, parser_chain=None,
|
||||
cookie_data=None, url=None, **unused_kwargs):
|
||||
"""Extracts event objects from the cookie.
|
||||
|
||||
Args:
|
||||
parser_context: A parser context object (instance of ParserContext).
|
||||
file_entry: Optional file entry object (instance of dfvfs.FileEntry).
|
||||
The default is None.
|
||||
parser_chain: Optional string containing the parsing chain up to this
|
||||
point. The default is None.
|
||||
cookie_data: The cookie data, as a byte string.
|
||||
url: The full URL or path where the cookie got set.
|
||||
"""
|
||||
# The structure of the field:
|
||||
# <domain hash>.<last time>.<sessions>.<sources>.<variables>
|
||||
fields = cookie_data.split('.')
|
||||
|
||||
if len(fields) > 5:
|
||||
variables = u'.'.join(fields[4:])
|
||||
fields = fields[0:4]
|
||||
fields.append(variables)
|
||||
|
||||
if len(fields) != 5:
|
||||
raise errors.WrongPlugin(u'Wrong number of fields. [{0:d} vs. 5]'.format(
|
||||
len(fields)))
|
||||
|
||||
domain_hash, last, sessions, sources, variables = fields
|
||||
extra_variables = variables.split(u'|')
|
||||
|
||||
kwargs = {}
|
||||
for variable in extra_variables:
|
||||
key, _, value = variable.partition(u'=')
|
||||
try:
|
||||
value_line = unicode(urllib.unquote(str(value)), 'utf-8')
|
||||
except UnicodeDecodeError:
|
||||
value_line = repr(value)
|
||||
|
||||
kwargs[key] = value_line
|
||||
|
||||
event_object = GoogleAnalyticsEvent(
|
||||
int(last, 10), eventdata.EventTimestamp.LAST_VISITED_TIME,
|
||||
url, 'utmz', self.COOKIE_NAME, domain_hash=domain_hash,
|
||||
sessions=int(sessions, 10), sources=int(sources, 10),
|
||||
**kwargs)
|
||||
parser_context.ProduceEvent(
|
||||
event_object, parser_chain=parser_chain, file_entry=file_entry)
|
||||
|
||||
|
||||
class GoogleAnalyticsUtmaPlugin(interface.CookiePlugin):
|
||||
"""A browser cookie plugin for Google Analytics cookies."""
|
||||
|
||||
NAME = 'google_analytics_utma'
|
||||
|
||||
COOKIE_NAME = u'__utma'
|
||||
|
||||
# Point to few sources for URL information.
|
||||
URLS = [
|
||||
(u'http://www.dfinews.com/articles/2012/02/'
|
||||
u'google-analytics-cookies-and-forensic-implications')]
|
||||
|
||||
def GetEntries(
|
||||
self, parser_context, file_entry=None, parser_chain=None,
|
||||
cookie_data=None, url=None, **unused_kwargs):
|
||||
"""Extracts event objects from the cookie.
|
||||
|
||||
Args:
|
||||
parser_context: A parser context object (instance of ParserContext).
|
||||
file_entry: Optional file entry object (instance of dfvfs.FileEntry).
|
||||
The default is None.
|
||||
parser_chain: Optional string containing the parsing chain up to this
|
||||
point. The default is None.
|
||||
cookie_data: The cookie data, as a byte string.
|
||||
url: The full URL or path where the cookie got set.
|
||||
"""
|
||||
# Values has the structure of:
|
||||
# <domain hash>.<visitor ID>.<first visit>.<previous>.<last>.<# of
|
||||
# sessions>
|
||||
fields = cookie_data.split(u'.')
|
||||
|
||||
# Check for a valid record.
|
||||
if len(fields) != 6:
|
||||
raise errors.WrongPlugin(u'Wrong number of fields. [{0:d} vs. 6]'.format(
|
||||
len(fields)))
|
||||
|
||||
domain_hash, visitor_id, first_visit, previous, last, sessions = fields
|
||||
|
||||
# TODO: Double check this time is stored in UTC and not local time.
|
||||
first_epoch = int(first_visit, 10)
|
||||
event_object = GoogleAnalyticsEvent(
|
||||
first_epoch, 'Analytics Creation Time', url, 'utma', self.COOKIE_NAME,
|
||||
domain_hash=domain_hash, visitor_id=visitor_id,
|
||||
sessions=int(sessions, 10))
|
||||
parser_context.ProduceEvent(
|
||||
event_object, parser_chain=parser_chain, file_entry=file_entry)
|
||||
|
||||
event_object = GoogleAnalyticsEvent(
|
||||
int(previous, 10), 'Analytics Previous Time', url, 'utma',
|
||||
self.COOKIE_NAME, domain_hash=domain_hash, visitor_id=visitor_id,
|
||||
sessions=int(sessions, 10))
|
||||
parser_context.ProduceEvent(
|
||||
event_object, parser_chain=parser_chain, file_entry=file_entry)
|
||||
|
||||
event_object = GoogleAnalyticsEvent(
|
||||
int(last, 10), eventdata.EventTimestamp.LAST_VISITED_TIME,
|
||||
url, 'utma', self.COOKIE_NAME, domain_hash=domain_hash,
|
||||
visitor_id=visitor_id, sessions=int(sessions, 10))
|
||||
parser_context.ProduceEvent(
|
||||
event_object, parser_chain=parser_chain, file_entry=file_entry)
|
||||
|
||||
|
||||
class GoogleAnalyticsUtmbPlugin(interface.CookiePlugin):
|
||||
"""A browser cookie plugin for Google Analytics cookies."""
|
||||
|
||||
NAME = 'google_analytics_utmb'
|
||||
|
||||
COOKIE_NAME = u'__utmb'
|
||||
|
||||
# Point to few sources for URL information.
|
||||
URLS = [
|
||||
(u'http://www.dfinews.com/articles/2012/02/'
|
||||
u'google-analytics-cookies-and-forensic-implications')]
|
||||
|
||||
def GetEntries(
|
||||
self, parser_context, file_entry=None, parser_chain=None,
|
||||
cookie_data=None, url=None, **unused_kwargs):
|
||||
"""Extracts event objects from the cookie.
|
||||
|
||||
Args:
|
||||
parser_context: A parser context object (instance of ParserContext).
|
||||
file_entry: Optional file entry object (instance of dfvfs.FileEntry).
|
||||
The default is None.
|
||||
parser_chain: Optional string containing the parsing chain up to this
|
||||
point. The default is None.
|
||||
cookie_data: The cookie data, as a byte string.
|
||||
url: The full URL or path where the cookie got set.
|
||||
"""
|
||||
# Values has the structure of:
|
||||
# <domain hash>.<pages viewed>.10.<last time>
|
||||
fields = cookie_data.split(u'.')
|
||||
|
||||
# Check for a valid record.
|
||||
if len(fields) != 4:
|
||||
raise errors.WrongPlugin(u'Wrong number of fields. [{0:d} vs. 4]'.format(
|
||||
len(fields)))
|
||||
|
||||
domain_hash, pages_viewed, _, last = fields
|
||||
|
||||
event_object = GoogleAnalyticsEvent(
|
||||
int(last, 10), eventdata.EventTimestamp.LAST_VISITED_TIME,
|
||||
url, 'utmb', self.COOKIE_NAME, domain_hash=domain_hash,
|
||||
pages_viewed=int(pages_viewed, 10))
|
||||
parser_context.ProduceEvent(
|
||||
event_object, parser_chain=parser_chain, file_entry=file_entry)
|
||||
@@ -0,0 +1,139 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2014 The Plaso Project Authors.
|
||||
# Please see the AUTHORS file for details on individual authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""Tests for the Google Analytics cookies."""
|
||||
|
||||
import unittest
|
||||
|
||||
# pylint: disable=unused-import
|
||||
from plaso.formatters import ganalytics as ganalytics_formatter
|
||||
from plaso.lib import eventdata
|
||||
from plaso.lib import timelib_test
|
||||
from plaso.parsers.cookie_plugins import ganalytics
|
||||
from plaso.parsers.sqlite_plugins import chrome_cookies
|
||||
from plaso.parsers.sqlite_plugins import firefox_cookies
|
||||
from plaso.parsers.sqlite_plugins import test_lib
|
||||
|
||||
|
||||
class GoogleAnalyticsPluginTest(test_lib.SQLitePluginTestCase):
|
||||
"""Tests for the Google Analytics plugin."""
|
||||
|
||||
def setUp(self):
|
||||
"""Sets up the needed objects used throughout the test."""
|
||||
|
||||
def _GetAnalyticsCookies(self, event_queue_consumer):
|
||||
"""Return a list of analytics cookies."""
|
||||
cookies = []
|
||||
for event_object in self._GetEventObjectsFromQueue(event_queue_consumer):
|
||||
if isinstance(event_object, ganalytics.GoogleAnalyticsEvent):
|
||||
cookies.append(event_object)
|
||||
return cookies
|
||||
|
||||
def testParsingFirefox29CookieDatabase(self):
|
||||
"""Tests the Process function on a Firefox 29 cookie database file."""
|
||||
plugin = firefox_cookies.FirefoxCookiePlugin()
|
||||
test_file = self._GetTestFilePath(['firefox_cookies.sqlite'])
|
||||
event_queue_consumer = self._ParseDatabaseFileWithPlugin(plugin, test_file)
|
||||
event_objects = self._GetAnalyticsCookies(event_queue_consumer)
|
||||
|
||||
self.assertEquals(len(event_objects), 25)
|
||||
|
||||
event_object = event_objects[14]
|
||||
|
||||
self.assertEquals(
|
||||
event_object.utmcct,
|
||||
u'/frettir/erlent/2013/10/30/maelt_med_kerfisbundnum_hydingum/')
|
||||
self.assertEquals(
|
||||
event_object.timestamp, timelib_test.CopyStringToTimestamp(
|
||||
'2013-10-30 21:56:06'))
|
||||
self.assertEquals(event_object.url, u'http://ads.aha.is/')
|
||||
self.assertEquals(event_object.utmcsr, u'mbl.is')
|
||||
|
||||
expected_msg = (
|
||||
u'http://ads.aha.is/ (__utmz) Sessions: 1 Domain Hash: 137167072 '
|
||||
u'Sources: 1 Last source used to access: mbl.is Ad campaign '
|
||||
u'information: (referral) Last type of visit: referral Path to '
|
||||
u'the page of referring link: /frettir/erlent/2013/10/30/'
|
||||
u'maelt_med_kerfisbundnum_hydingum/')
|
||||
|
||||
self._TestGetMessageStrings(
|
||||
event_object, expected_msg, u'http://ads.aha.is/ (__utmz)')
|
||||
|
||||
def testParsingChromeCookieDatabase(self):
|
||||
"""Test the process function on a Chrome cookie database."""
|
||||
plugin = chrome_cookies.ChromeCookiePlugin()
|
||||
test_file = self._GetTestFilePath(['cookies.db'])
|
||||
event_queue_consumer = self._ParseDatabaseFileWithPlugin(plugin, test_file)
|
||||
event_objects = self._GetAnalyticsCookies(event_queue_consumer)
|
||||
|
||||
# The cookie database contains 560 entries in total. Out of them
|
||||
# there are 75 events created by the Google Analytics plugin.
|
||||
self.assertEquals(len(event_objects), 75)
|
||||
# Check few "random" events to verify.
|
||||
|
||||
# Check an UTMZ Google Analytics event.
|
||||
event_object = event_objects[39]
|
||||
self.assertEquals(event_object.utmctr, u'enders game')
|
||||
self.assertEquals(event_object.domain_hash, u'68898382')
|
||||
self.assertEquals(event_object.sessions, 1)
|
||||
|
||||
expected_msg = (
|
||||
u'http://imdb.com/ (__utmz) Sessions: 1 Domain Hash: 68898382 '
|
||||
u'Sources: 1 Last source used to access: google Ad campaign '
|
||||
u'information: (organic) Last type of visit: organic Keywords '
|
||||
u'used to find site: enders game')
|
||||
self._TestGetMessageStrings(
|
||||
event_object, expected_msg, u'http://imdb.com/ (__utmz)')
|
||||
|
||||
# Check the UTMA Google Analytics event.
|
||||
event_object = event_objects[41]
|
||||
self.assertEquals(event_object.timestamp_desc, u'Analytics Previous Time')
|
||||
self.assertEquals(event_object.cookie_name, u'__utma')
|
||||
self.assertEquals(event_object.visitor_id, u'1827102436')
|
||||
self.assertEquals(event_object.sessions, 2)
|
||||
|
||||
expected_timestamp = timelib_test.CopyStringToTimestamp(
|
||||
'2012-03-22 01:55:29')
|
||||
self.assertEquals(event_object.timestamp, expected_timestamp)
|
||||
|
||||
expected_msg = (
|
||||
u'http://assets.tumblr.com/ (__utma) Sessions: 2 Domain Hash: '
|
||||
u'151488169 Visitor ID: 151488169')
|
||||
self._TestGetMessageStrings(
|
||||
event_object, expected_msg, u'http://assets.tumblr.com/ (__utma)')
|
||||
|
||||
# Check the UTMB Google Analytics event.
|
||||
event_object = event_objects[34]
|
||||
self.assertEquals(
|
||||
event_object.timestamp_desc, eventdata.EventTimestamp.LAST_VISITED_TIME)
|
||||
self.assertEquals(event_object.cookie_name, u'__utmb')
|
||||
self.assertEquals(event_object.domain_hash, u'154523900')
|
||||
self.assertEquals(event_object.pages_viewed, 1)
|
||||
|
||||
expected_timestamp = timelib_test.CopyStringToTimestamp(
|
||||
'2012-03-22 01:48:30')
|
||||
self.assertEquals(event_object.timestamp, expected_timestamp)
|
||||
|
||||
expected_msg = (
|
||||
u'http://upressonline.com/ (__utmb) Pages Viewed: 1 Domain Hash: '
|
||||
u'154523900')
|
||||
self._TestGetMessageStrings(
|
||||
event_object, expected_msg, u'http://upressonline.com/ (__utmb)')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2013 The Plaso Project Authors.
|
||||
# Please see the AUTHORS file for details on individual authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""This file contains an interface for browser cookie plugins."""
|
||||
|
||||
import abc
|
||||
|
||||
from plaso.lib import errors
|
||||
from plaso.lib import registry
|
||||
from plaso.parsers import plugins
|
||||
|
||||
|
||||
# TODO: move this into the parsers and plugins manager.
|
||||
def GetPlugins():
|
||||
"""Returns a list of all cookie plugins."""
|
||||
plugins_list = []
|
||||
for plugin_cls in CookiePlugin.classes.itervalues():
|
||||
parent_name = getattr(plugin_cls, 'parent_class_name', 'NOTHERE')
|
||||
if parent_name != 'cookie':
|
||||
continue
|
||||
|
||||
plugins_list.append(plugin_cls())
|
||||
|
||||
return plugins_list
|
||||
|
||||
|
||||
class CookiePlugin(plugins.BasePlugin):
|
||||
"""A browser cookie plugin for Plaso.
|
||||
|
||||
This is a generic cookie parsing interface that can handle parsing
|
||||
cookies from all browsers.
|
||||
"""
|
||||
__metaclass__ = registry.MetaclassRegistry
|
||||
__abstract = True
|
||||
|
||||
NAME = 'cookie'
|
||||
|
||||
# The name of the cookie value that this plugin is designed to parse.
|
||||
# This value is used to evaluate whether the plugin is the correct one
|
||||
# to parse the browser cookie.
|
||||
COOKIE_NAME = u''
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the browser cookie plugin."""
|
||||
super(CookiePlugin, self).__init__()
|
||||
self.cookie_data = ''
|
||||
|
||||
@abc.abstractmethod
|
||||
def GetEntries(
|
||||
self, parser_context, file_entry=None, parser_chain=None,
|
||||
cookie_data=None, url=None, **kwargs):
|
||||
"""Extract and return EventObjects from the data structure.
|
||||
|
||||
Args:
|
||||
parser_context: A parser context object (instance of ParserContext).
|
||||
file_entry: Optional file entry object (instance of dfvfs.FileEntry).
|
||||
The default is None.
|
||||
parser_chain: Optional string containing the parsing chain up to this
|
||||
point. The default is None.
|
||||
cookie_data: Optional cookie data, as a byte string.
|
||||
url: Optional URL or path where the cookie got set.
|
||||
"""
|
||||
|
||||
def Process(
|
||||
self, parser_context, file_entry=None, parser_chain=None,
|
||||
cookie_name=None, cookie_data=None, url=None, **kwargs):
|
||||
"""Determine if this is the right plugin for this cookie.
|
||||
|
||||
Args:
|
||||
parser_context: A parser context object (instance of ParserContext).
|
||||
file_entry: Optional file entry object (instance of dfvfs.FileEntry).
|
||||
The default is None.
|
||||
parser_chain: Optional string containing the parsing chain up to this
|
||||
point. The default is None.
|
||||
cookie_name: The name of the cookie value.
|
||||
cookie_data: The cookie data, as a byte string.
|
||||
url: The full URL or path where the cookie got set.
|
||||
|
||||
Raises:
|
||||
errors.WrongPlugin: If the cookie name differs from the one
|
||||
supplied in COOKIE_NAME.
|
||||
ValueError: If cookie_name or cookie_data are not set.
|
||||
"""
|
||||
if cookie_name is None or cookie_data is None:
|
||||
raise ValueError(u'Cookie name or data are not set.')
|
||||
|
||||
if cookie_name != self.COOKIE_NAME:
|
||||
raise errors.WrongPlugin(
|
||||
u'Not the correct cookie plugin for: {0:s} [{1:s}]'.format(
|
||||
cookie_name, self.NAME))
|
||||
|
||||
# This will raise if unhandled keyword arguments are passed.
|
||||
super(CookiePlugin, self).Process(parser_context, **kwargs)
|
||||
|
||||
# Add ourselves to the parser chain, which will be used in all subsequent
|
||||
# event creation in this parser.
|
||||
parser_chain = self._BuildParserChain(parser_chain)
|
||||
|
||||
self.GetEntries(
|
||||
parser_context, file_entry=file_entry, parser_chain=parser_chain,
|
||||
cookie_data=cookie_data, url=url)
|
||||
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2014 The Plaso Project Authors.
|
||||
# Please see the AUTHORS file for details on individual authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""Browser cookie plugin related functions and classes for testing."""
|
||||
|
||||
from plaso.parsers import test_lib
|
||||
|
||||
|
||||
class CookiePluginTestCase(test_lib.ParserTestCase):
|
||||
"""The unit test case for a browser cookie plugin."""
|
||||
Reference in New Issue
Block a user