diff --git a/pastepwn/analyzers/logicalanalyzers/__init__.py b/pastepwn/analyzers/logicalanalyzers/__init__.py new file mode 100644 index 0000000..b664eca --- /dev/null +++ b/pastepwn/analyzers/logicalanalyzers/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- + +from .andanalyzer import AndAnalyzer +from .logicalbaseanalyzer import LogicalBaseAnalyzer +from .oranalyzer import OrAnalyzer + +__all__ = ('LogicalBaseAnalyzer', 'AndAnalyzer', 'OrAnalyzer') diff --git a/pastepwn/analyzers/logicalanalyzers/andanalyzer.py b/pastepwn/analyzers/logicalanalyzers/andanalyzer.py new file mode 100644 index 0000000..b71d496 --- /dev/null +++ b/pastepwn/analyzers/logicalanalyzers/andanalyzer.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from .logicalbaseanalyzer import LogicalBaseAnalyzer + + +class AndAnalyzer(LogicalBaseAnalyzer): + """Meta analyzer which matches a paste if all of the passed analyzers match that paste""" + name = "AndAnalyzer" + + def match(self, paste): + """Returns True if all of the passed analyzers matched""" + if len(self.analyzers) == 0: + return False + + for analyzer in self.analyzers: + if not analyzer.match(paste): + return False + + return True diff --git a/pastepwn/analyzers/logicalanalyzers/logicalbaseanalyzer.py b/pastepwn/analyzers/logicalanalyzers/logicalbaseanalyzer.py new file mode 100644 index 0000000..b8f674e --- /dev/null +++ b/pastepwn/analyzers/logicalanalyzers/logicalbaseanalyzer.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +from pastepwn.analyzers.basicanalyzer import BasicAnalyzer +from pastepwn.util import listify + + +class LogicalBaseAnalyzer(BasicAnalyzer): + """Meta analyzer used to combine analyzers via a logical operator""" + name = "LogicalBaseAnalyzer" + + def __init__(self, actions, analyzers, merge_actions=False): + """ + Meta analyzer used to combine analyzers via a logical operator + :param actions: A single action or a list of actions to be executed on every paste + :param analyzers: A single analyzer or a list of analyzers used to match pastes + """ + super().__init__(actions) + self.analyzers = listify(analyzers) + + if len(self.analyzers) == 0: + self.logger.warning("You have not specified any analyzers inside '{}'".format(self.name)) + + if merge_actions: + self._merge_actions() + + def _merge_actions(self): + """ + Merges the actions of the passed analyzers into this meta analyzer + If merged, the actions will be executed if this meta analyzer matches + :return: None + """ + for analyzer in self.analyzers: + for action in analyzer.actions: + self.actions.append(action) + + def add_analyzer(self, analyzer): + """ + Add a new analyzer to the list of analyzers + :param analyzer: A single analyzer used to match pastes + :return: + """ + self.analyzers.append(analyzer) + + def match(self, paste): + """Must be overridden by the subclasses""" + raise NotImplementedError("match() function must be overridden in the subclasses!") diff --git a/pastepwn/analyzers/logicalanalyzers/oranalyzer.py b/pastepwn/analyzers/logicalanalyzers/oranalyzer.py new file mode 100644 index 0000000..5e2ce6b --- /dev/null +++ b/pastepwn/analyzers/logicalanalyzers/oranalyzer.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +from .logicalbaseanalyzer import LogicalBaseAnalyzer + + +class OrAnalyzer(LogicalBaseAnalyzer): + """Meta analyzer which matches a paste if any of the passed analyzers match that paste""" + name = "OrAnalyzer" + + def match(self, paste): + """Returns True if all of the passed analyzers matched""" + for analyzer in self.analyzers: + if analyzer.match(paste): + return True + + return False diff --git a/pastepwn/analyzers/logicalanalyzers/tests/__init__.py b/pastepwn/analyzers/logicalanalyzers/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pastepwn/analyzers/logicalanalyzers/tests/andanalyzer_test.py b/pastepwn/analyzers/logicalanalyzers/tests/andanalyzer_test.py new file mode 100644 index 0000000..7693e43 --- /dev/null +++ b/pastepwn/analyzers/logicalanalyzers/tests/andanalyzer_test.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +import unittest +from unittest import mock + +from pastepwn.analyzers import AndAnalyzer + + +class TestAndAnalyzer(unittest.TestCase): + def setUp(self): + self.obj = mock.Mock() + self.gt = mock.Mock() + self.gt.match = mock.Mock(return_value=True) + + self.gf = mock.Mock() + self.gf.match = mock.Mock(return_value=False) + + def test_match_positive(self): + self.obj.body = "Test" + analyzer = AndAnalyzer(None, [self.gt]) + self.assertTrue(analyzer.match(self.obj)) + + analyzer = AndAnalyzer(None, [self.gt, self.gt, self.gt]) + self.assertTrue(analyzer.match(self.obj)) + + # Check if the analyzer returns false if there is at least one false result + analyzer = AndAnalyzer([], [self.gt, self.gf]) + self.assertFalse(analyzer.match(self.obj)) + + analyzer = AndAnalyzer([], [self.gf, self.gf]) + self.assertFalse(analyzer.match(self.obj)) + + analyzer = AndAnalyzer([], [self.gf, self.gf, self.gf, self.gt]) + self.assertFalse(analyzer.match(self.obj)) + + def test_negative(self): + self.obj.body = "" + + analyzer = AndAnalyzer([], None) + self.assertFalse(analyzer.match(self.obj)) + + analyzer = AndAnalyzer([], []) + self.assertFalse(analyzer.match(self.obj)) + + def test_actions_present(self): + analyzer = AndAnalyzer(self.obj, None) + self.assertEqual([self.obj], analyzer.actions) + + def test_analyzers_present(self): + analyzer = AndAnalyzer(None, self.obj) + self.assertEqual([self.obj], analyzer.analyzers) + + +if __name__ == '__main__': + unittest.main() diff --git a/pastepwn/analyzers/logicalanalyzers/tests/logicalbaseanalyzer_test.py b/pastepwn/analyzers/logicalanalyzers/tests/logicalbaseanalyzer_test.py new file mode 100644 index 0000000..780d99a --- /dev/null +++ b/pastepwn/analyzers/logicalanalyzers/tests/logicalbaseanalyzer_test.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +import unittest +from unittest import mock + +from pastepwn.analyzers.logicalanalyzers import LogicalBaseAnalyzer + + +class TestLogicalBaseAnalyzer(unittest.TestCase): + def setUp(self): + self.obj = mock.Mock() + + def test_exception(self): + analyzer = LogicalBaseAnalyzer([], []) + self.assertRaises(NotImplementedError, analyzer.match, mock.Mock()) + + def test_actions_present(self): + analyzer = LogicalBaseAnalyzer(self.obj, None) + self.assertEqual([self.obj], analyzer.actions) + + def test_analyzers_present(self): + analyzer = LogicalBaseAnalyzer(None, self.obj) + self.assertEqual([self.obj], analyzer.analyzers) + + def test_merge_actions(self): + action1 = mock.Mock() + action2 = mock.Mock() + action3 = mock.Mock() + + analyzer1 = mock.Mock() + analyzer1.actions = [action1, action2] + analyzer2 = mock.Mock() + analyzer2.actions = [action3] + + analyzer = LogicalBaseAnalyzer(analyzers=[analyzer1, analyzer2], actions=[], merge_actions=True) + self.assertEqual(3, len(analyzer.actions), "Wrong amount of actions in LogicalBaseAnalyzer!") + self.assertEqual([action1, action2, action3], analyzer.actions, "Actions do not match!") + + +if __name__ == '__main__': + unittest.main() diff --git a/pastepwn/analyzers/logicalanalyzers/tests/oranalyzer_test.py b/pastepwn/analyzers/logicalanalyzers/tests/oranalyzer_test.py new file mode 100644 index 0000000..a14deaf --- /dev/null +++ b/pastepwn/analyzers/logicalanalyzers/tests/oranalyzer_test.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +import unittest +from unittest import mock + +from pastepwn.analyzers.logicalanalyzers import OrAnalyzer + + +class TestOrAnalyzer(unittest.TestCase): + def setUp(self): + self.obj = mock.Mock() + self.gt = mock.Mock() + self.gt.match = mock.Mock(return_value=True) + + self.gf = mock.Mock() + self.gf.match = mock.Mock(return_value=False) + + def test_match_positive(self): + self.obj.body = "Test" + analyzer = OrAnalyzer(None, [self.gt]) + self.assertTrue(analyzer.match(self.obj)) + + analyzer = OrAnalyzer(None, [self.gt, self.gt, self.gt]) + self.assertTrue(analyzer.match(self.obj)) + + analyzer = OrAnalyzer([], [self.gf, self.gf]) + self.assertFalse(analyzer.match(self.obj)) + + analyzer = OrAnalyzer([], [self.gf, self.gf, self.gf]) + self.assertFalse(analyzer.match(self.obj)) + + analyzer = OrAnalyzer([], [self.gf, self.gf, self.gf, self.gt]) + self.assertTrue(analyzer.match(self.obj)) + + def test_negative(self): + self.obj.body = "" + + analyzer = OrAnalyzer([], None) + self.assertFalse(analyzer.match(self.obj)) + + analyzer = OrAnalyzer([], []) + self.assertFalse(analyzer.match(self.obj)) + + def test_actions_present(self): + analyzer = OrAnalyzer(self.obj, None) + self.assertEqual([self.obj], analyzer.actions) + + def test_analyzers_present(self): + analyzer = OrAnalyzer(None, self.obj) + self.assertEqual([self.obj], analyzer.analyzers) + + +if __name__ == '__main__': + unittest.main()