from common import RostTestClass from pychrysalide.analysis.contents import MemoryContent from pychrysalide.analysis.scan import ContentScanner from pychrysalide.analysis.scan import ScanOptions from pychrysalide.analysis.scan.patterns.backends import AcismBackend from pychrysalide.analysis.scan.patterns.backends import BitapBackend class TestRostFuzzingFixes(RostTestClass): """TestCases to remember all the fixes for crashes identified by fuzzing.""" def testEmptyPatternListWithContent(self): """Check no backend is run if there is no pattern to look for.""" content = MemoryContent(b'\n') rule = ''' ''' backends = [ AcismBackend, # This one was segfaulting BitapBackend, ] for b in backends: options = ScanOptions() options.backend_for_data = b scanner = ContentScanner(rule) ctx = scanner.analyze(options, content) self.assertIsNotNone(ctx) def testMandatoryCondition(self): """Ensure a condition section exists in a rule.""" rule = ''' rule test { } ''' with self.assertRaisesRegex(ValueError, 'Unable to create content scanner'): scanner = ContentScanner(rule) def testNonExistingPattern(self): """Avoid to count the matches of a non-existing pattern.""" rule = ''' rule test { condition: #badid } ''' with self.assertRaisesRegex(ValueError, 'Unable to create content scanner'): scanner = ContentScanner(rule) def testNamespacesWithoutReductionCode(self): """Clean the code for ROST namespaces.""" rule = ''' rule test { condition: console } ''' self.check_rule_failure(rule) def testCallOnNonCallable(self): """Reject calls on non callable expressions softly.""" rule = ''' rule test { condition: console.log().log() } ''' self.check_rule_failure(rule) def testSelfReferencingRule(self): """Reject any rule referencing itself as match condition.""" rule = ''' rule test { condition: test } ''' self.check_rule_failure(rule) def testSelfReferencingRule(self): """Expect only one argument for the not operator, even in debug mode.""" rule = ''' rule test { condition: not(0) } ''' self.check_rule_success(rule) def testNoCommon(self): """Handle the case where no common item is found from an empty set.""" rule = ''' rule test { bytes: $a = "a" condition: maxcommon($a) == 0 } ''' self.check_rule_success(rule) def testAAsAcharacter(self): """Consider the 'a' character as a valid lowercase character.""" rule = ''' rule test { bytes: $a = "0000a0I0" nocase condition: $a } ''' self.check_rule_failure(rule) def testAAsAcharacter(self): """Do not expect initialized trackers when there is no real defined search pattern.""" rule = ''' rule test { bytes: $a = {[0]} condition: $a } ''' with self.assertRaisesRegex(ValueError, 'Unable to create content scanner'): scanner = ContentScanner(rule) def testAllocations(self): """Handle big alloctions for strings in conditions with regular expressions.""" rule = ''' rule test { condition: "%s" == "%s" } ''' % ("0" * (256 * 2 + 8), "0" * (256 * 2 + 8)) self.check_rule_success(rule) def testFileFinalAccess(self): """Ensure patterns found at the edges of scanned content do not crash the scanner.""" cnt = MemoryContent(bytes([ 0 for i in range(16) ])) rule = ''' rule test { bytes: $a = { 00 00 00 00 00 00 00 00 } condition: $a } ''' self.check_rule_success(rule, cnt) def testValidHexRangeMerge(self): """Merge valid hexadecimal ranges.""" rule = ''' rule test { bytes: $a = { [0] ?? } condition: $a } ''' with self.assertRaisesRegex(ValueError, 'Unable to create content scanner'): scanner = ContentScanner(rule) rule = ''' rule test { bytes: $a = { [2] ?? } condition: $a } ''' self.check_rule_failure(rule) def testSmallBase64(self): """Handle small base64 encodings which may produce few patterns.""" rule = ''' rule test { bytes: $a = "0" base64 condition: $a } ''' self.check_rule_failure(rule) def testCountIndex(self): """Ban pattern count indexes from the grammer.""" rule = ''' rule test { bytes: $a = "1" condition: #*[0] } ''' with self.assertRaisesRegex(ValueError, 'Unable to create content scanner'): scanner = ContentScanner(rule)