Skip to content

Commit

Permalink
Merge pull request #66 from d-Rickyy-b/dev
Browse files Browse the repository at this point in the history
Multiple fixes and few new features
  • Loading branch information
d-Rickyy-b authored Sep 4, 2019
2 parents b81ea3b + 8762ddd commit 4cce3a0
Show file tree
Hide file tree
Showing 12 changed files with 346 additions and 23 deletions.
2 changes: 1 addition & 1 deletion pastepwn/analyzers/bcrypthashanalyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ class BcryptHashAnalyzer(RegexAnalyzer):
name = "BcryptHashAnalyzer"

def __init__(self, actions):
regex = r"\$2[ayb]\$.{56}"
regex = r"\$2[ayb]\$[\d]{2}\$[./A-Za-z0-9]{53}"
super().__init__(actions, regex)
4 changes: 3 additions & 1 deletion pastepwn/analyzers/genericanalyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ def __init__(self, actions, match_func):

if match_func is None:
raise ValueError("Function to be called cannot be None")
elif not callable(match_func):
raise ValueError("Function you provided isn't callable")

self.match_func = match_func

Expand All @@ -22,6 +24,6 @@ def match(self, paste):
result = self.match_func(paste)
except Exception as e:
result = False
logging.getLogger(__name__).warning("Executing custom match function raised an exception! {}".format(e))
logging.getLogger(__name__).warning("Executing custom match function '{}' raised an exception! {}".format(self.match_func.__name__, e))

return result
3 changes: 3 additions & 0 deletions pastepwn/analyzers/regexanalyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@ def __init__(self, actions, regex):

def match(self, paste):
"""Match the content of a paste via regex. Return true if regex matches"""
if paste is None:
return False

paste_content = paste.body or ""
return self.regex.search(paste_content) is not None
59 changes: 59 additions & 0 deletions pastepwn/analyzers/tests/bcrypthashanalyzer_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
import unittest
from unittest import mock

from pastepwn.analyzers.bcrypthashanalyzer import BcryptHashAnalyzer


class TestBcryptHashAnalyzer(unittest.TestCase):
def setUp(self):
self.analyzer = BcryptHashAnalyzer(None)
self.obj = mock.Mock()

def test_match(self):
valid_hashes = ["$2a$10$BIgnlSmYE8qYiONM0NQ53eRWBw5G4HIJEbXKzcsRVt.08IDnqH/V.",
"$2a$11$EppiRqR0kG9EKy56edDWTOnsv/oGW0dqAJB9ucmn3augbmcm8v/iy",
"$2b$10$FpOpno43SIE8e1hWnlOdR.9hG2J8dd5FD1kQq8hn4zLdKa5eIiFUO",
"$2a$10$SVy7GlMnWsemiZByHSnV0O3WoEHGImFt8v07uH.K3ZXwH5j9o/DP.",
"$2a$10$59SNkcZ0rdC2VgeWaavVyea9PFget/xmtbV7.9IeJl3CUq.Q954i2",
"$2b$10$Y8CJ9YIwrxt1YYgMqqU1delCYoTpIl18SRtYYI2kyM3jduKPHvWMC",
"$2a$10$2tNCUb.FUpSutyhkbqmMBuNnLzhqI4q9Miqurnj6eu.XsiIjww7I6",
"$2a$10$OyrADUFmj9QEqsd8frkEDOEYSPQalW5qoI1s2z6taCWwgUsjKzk5m"]

invalid_hashes = ["7168D46050573DDA4CE409FA1515638BD28E86346D45F686310ED0678172BABCD4117FD15DD380B964352FE879FB745B573A730D526BB1188B2790FBA06E8ACA",
"5FD924625F6AB16A19CC9807C7C506AE1813490E4BA675F843D5A10E0BAACDB8",
"522F02FEA11E70C03C90C247C50410443246BFCB",
"8433FD5A3B0ED71D21CFB9F291BD89B9",
"$2a$124$SVy7GlMnWsemiZByHSnV0O3WoEHGImFt8v07uH.K3ZXwH5j9o/DP.a",
"$2a$12$SVy7GlMnWsemiZByHSnV0O3WoEHGImFt8v{07uH.K3ZXwH5j9o/DP.a",
"$2a$14$SVy7GlMnWsemiZByHSnV0O3WGImFt8v07uH.K3ZXwH5j9o/DP.a",
"@x,Y8q+jnYeZr$;",
"This is a test",
"$2a$10$ asdf 1234 how are you?"]

for test_hash in valid_hashes:
self.obj.body = test_hash
self.assertTrue(self.analyzer.match(self.obj), test_hash)

for test_hash in invalid_hashes:
self.obj.body = test_hash
self.assertFalse(self.analyzer.match(self.obj), test_hash)

def test_match_none(self):
self.obj.body = None
self.assertFalse(self.analyzer.match(self.obj))

self.obj = None
self.assertFalse(self.analyzer.match(self.obj))

def test_match_empty(self):
self.obj.body = ""
self.assertFalse(self.analyzer.match(self.obj))

def test_actions_present(self):
analyzer = BcryptHashAnalyzer(self.obj)
self.assertEqual([self.obj], analyzer.actions)


if __name__ == '__main__':
unittest.main()
136 changes: 136 additions & 0 deletions pastepwn/analyzers/tests/wordanalyzer_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# -*- coding: utf-8 -*-
import unittest
from unittest import mock

from pastepwn.analyzers.wordanalyzer import WordAnalyzer


class TestWordAnalyzer(unittest.TestCase):
def setUp(self):
self.obj = mock.Mock()

def test_match(self):
analyzer = WordAnalyzer(None, "Test")
self.obj.body = "This is a Test"
self.assertTrue(analyzer.match(self.obj))

analyzer = WordAnalyzer(None, "Test")
self.obj.body = "There are tests for everything"
self.assertTrue(analyzer.match(self.obj))

self.obj.body = "This is a Test for a longer sentence"
self.assertTrue(analyzer.match(self.obj))

self.obj.body = "Completely unrelated"
self.assertFalse(analyzer.match(self.obj))

def test_blacklist(self):
blacklist = ["fake", "bad"]
analyzer = WordAnalyzer(None, "Test", blacklist=blacklist)

self.obj.body = "This is a Test"
self.assertTrue(analyzer.match(self.obj))

self.obj.body = "This is a fake Test"
self.assertFalse(analyzer.match(self.obj))

analyzer = WordAnalyzer(None, "Test", blacklist=blacklist, case_sensitive=True)

self.obj.body = "This is a Test"
self.assertTrue(analyzer.match(self.obj))

self.obj.body = "This is a Fake Test"
self.assertTrue(analyzer.match(self.obj))

def test_multiple_words(self):
analyzer = WordAnalyzer(None, None)
self.assertEqual(analyzer.words, [])

analyzer = WordAnalyzer(None, ["My", "first", "Test"])
self.obj.body = "This is a little test for something"
self.assertTrue(analyzer.match(self.obj))

self.obj.body = "You are my best friend so far!"
self.assertTrue(analyzer.match(self.obj))

self.obj.body = "This is the first time I try this"
self.assertTrue(analyzer.match(self.obj))

self.obj.body = "This time we try to match multiple words/tests for the first time."
self.assertTrue(analyzer.match(self.obj))

self.obj.body = "None of the words are contained!"
self.assertFalse(analyzer.match(self.obj))

# Check for case sensitivity for multiple words
analyzer2 = WordAnalyzer(None, ["My", "first", "Test"], case_sensitive=True)

self.obj.body = "That's not my issue!"
self.assertFalse(analyzer2.match(self.obj))

self.obj.body = "That's not My issue!"
self.assertTrue(analyzer2.match(self.obj))

def test_add_word(self):
analyzer = WordAnalyzer(None, "Test")
self.assertEqual(len(analyzer.words), 1)
self.assertEqual(analyzer.words, ["Test"])

analyzer.add_word("second")
self.assertEqual(len(analyzer.words), 2)
self.assertEqual(analyzer.words, ["Test", "second"])

def test_case_sensitive(self):
analyzer = WordAnalyzer(None, "Test", case_sensitive=True)
self.obj.body = "This is a Test for case sensitivity"
self.assertTrue(analyzer.match(self.obj))

self.obj.body = "This is a test for case sensitivity"
self.assertFalse(analyzer.match(self.obj))

self.obj.body = "This is a tESt for case sensitivity"
self.assertFalse(analyzer.match(self.obj))

analyzer2 = WordAnalyzer(None, "Te1st")
self.obj.body = "This is a te1st for case sensitivity"
self.assertTrue(analyzer2.match(self.obj))

analyzer2 = WordAnalyzer(None, "Te1st")
self.obj.body = "This is a tE1st for case sensitivity"
self.assertTrue(analyzer2.match(self.obj))

def test_multiple_case_sensitive(self):
"""Test if it's possible to match any of multiple words in a wordanalyzer when case sensitivty is activated"""
analyzer = WordAnalyzer(None, ["My", "first", "Test"], case_sensitive=True)
self.obj.body = "This is a little test for something"
self.assertFalse(analyzer.match(self.obj))

self.obj.body = "You are my best friend so far!"
self.assertFalse(analyzer.match(self.obj))

self.obj.body = "This is a Test for case sensitivity"
self.assertTrue(analyzer.match(self.obj))

self.obj.body = "This is a test for case sensitivity. It's the first of its kind."
self.assertTrue(analyzer.match(self.obj))

def test_match_none(self):
analyzer = WordAnalyzer(None, "Test")
self.obj.body = None
self.assertFalse(analyzer.match(self.obj))

self.obj = None
self.assertFalse(analyzer.match(self.obj))

def test_match_empty(self):
analyzer = WordAnalyzer(None, "Test")
self.obj.body = ""
self.assertFalse(analyzer.match(self.obj))

def test_actions_present(self):
analyzer = WordAnalyzer(self.obj, "Test")
self.assertEqual([self.obj], analyzer.actions)


if __name__ == '__main__':
unittest.main()
40 changes: 31 additions & 9 deletions pastepwn/analyzers/wordanalyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,24 @@


class WordAnalyzer(BasicAnalyzer):
"""Analyzer to match the content of a paste via regular expressions"""
"""Analyzer to match the content of a paste by words"""
name = "WordAnalyzer"

def __init__(self, actions, word, blacklist=None, case_sensitive=False):
super().__init__(actions, "{0} ({1})".format(self.name, word))
self.word = word
def __init__(self, actions, words, blacklist=None, case_sensitive=False):
super().__init__(actions, "{0} ({1})".format(self.name, words))

if words is None:
self.words = []
elif isinstance(words, list):
self.words = words
else:
self.words = [words]

self.blacklist = blacklist or []
self.case_sensitive = case_sensitive

def _blacklist_word_found(self, text):
if self.case_sensitive:
if not self.case_sensitive:
text = text.lower()
self.blacklist = [x.lower() for x in self.blacklist]

Expand All @@ -23,14 +30,29 @@ def _blacklist_word_found(self, text):

return False

def add_word(self, word):
"""Add a word to the analyzer"""
self.words.append(word)

def match(self, paste):
"""Check if the specified word is part of the paste text"""
paste_content = paste.body
"""Check if the specified words are part of the paste text"""
if paste is None:
return False

paste_content = paste.body or ""

if self._blacklist_word_found(paste_content):
return False

if self.case_sensitive:
return self.word in paste_content
for word in self.words:
# Never use 'return word in paste_content' - otherwise you will
# return false before all words have been checked
if word in paste_content:
return True
else:
return self.word.lower() in paste_content.lower()
for word in self.words:
if word.lower() in paste_content.lower():
return True

return False
6 changes: 5 additions & 1 deletion pastepwn/core/pastedispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@


class PasteDispatcher(object):
"""The PasteDispatcher dispatches the downloaded pastes to the analyzers"""

def __init__(self, paste_queue, action_queue=None, exception_event=None):
self.logger = logging.getLogger(__name__)
Expand All @@ -28,7 +29,9 @@ def _pool_thread(self):
pass

def add_analyzer(self, analyzer):
self.analyzers.append(analyzer)
"""Adds an analyzer to the list of analyzers"""
with self.__lock:
self.analyzers.append(analyzer)

def start(self, workers=4, ready=None):
"""Starts dispatching the downloaded pastes to the list of analyzers"""
Expand Down Expand Up @@ -81,6 +84,7 @@ def _process_paste(self, paste):
self.action_queue.put((actions, paste, analyzer))

def stop(self):
"""Stops dispatching pastes to the analyzers"""
self.__stop_event.set()
while self.running:
sleep(0.1)
Expand Down
Loading

0 comments on commit 4cce3a0

Please sign in to comment.