def test_matching(self): """Creates SH with signal, attempts to retrieve w/ search.""" SH = SignalHolder(self.test_signal) matching = SH.find(slugs=self.test_signal.slug) self.assertIsInstance(matching, SignalHolder) self.assertEqual(1, len(matching)) self._assert_same_signal(self.test_signal, matching[0])
def test_register_duplicate_signal(self): """Creates empty SH, tries registering dupe signal.""" SH = SignalHolder() SH.register(self.test_signal) SH.register(self.test_signal) self.assertEqual(1, len(SH)) self._assert_same_signal(self.test_signal, SH[0])
def test_register_signal_list(self): """Creates empty SH, registers signal list, checks for presence.""" SH = SignalHolder() SH.register([self.test_signal, self.test_signal2]) self.assertEqual(2, len(SH)) self._assert_same_signal(self.test_signal, SH[0]) self._assert_same_signal(self.test_signal2, SH[1])
def test_register_SH(self): """Creates empty SH, registers SH w/ signals, checks for presence.""" SH = SignalHolder([self.test_signal, self.test_signal2]) SH2 = SignalHolder() SH2.register(SH) self.assertEqual(2, len(SH2)) self._assert_same_signal(self.test_signal, SH2[0]) self._assert_same_signal(self.test_signal2, SH2[1])
def test_init_SH(self): """Creates SignalHolder with SH of signals, checks for presence.""" SH = SignalHolder([self.test_signal, self.test_signal2]) SH2 = SignalHolder(SH) self.assertEqual(2, len(SH)) self.assertEqual(2, len(SH2)) self._assert_same_signal(self.test_signal, SH2[0]) self._assert_same_signal(self.test_signal2, SH2[1])
def test_is_equal(self): """Tests 'equality' of two SignalHolders.""" SH1 = SignalHolder(self.test_signal) SH2 = SignalHolder(self.test_signal) self.assertEqual(SH1, SH2) self.assertEqual(SH1, SH2) SH2 = SignalHolder(self.test_signal2) self.assertNotEqual(SH1, SH2) self.assertNotEqual(SH2, SH1)
def test_SH_repr(self): """Creates a SH with signal, checks __repr__ value.""" SH = SignalHolder(self.test_signal) self.assertEqual(1, len(SH)) self.assertEqual('["TEST_SIGNAL"]', str(SH)) SH.register(self.test_signal2) self.assertEqual(2, len(SH)) self.assertEqual('["TEST_SIGNAL", "TEST_SIGNAL2"]', str(SH))
def test_compare(self): """Tests 'compare' method to see if two SignalHolders differ.""" SH1 = SignalHolder(self.test_signal) SH2 = SignalHolder(self.test_signal) data = {"is_diff": False, "sh1_len": 1, "sh2_len": 1, "sh1_not_in_sh2": SignalHolder(), "sh2_not_in_sh1": SignalHolder()} self.assertEqual(data, SH1.compare(SH2)) SH2 = SignalHolder(self.test_signal2) self.assertNotEqual(data, SH1.compare(SH2))
def test_compare(self): """Tests 'compare' method to see if two SignalHolders differ.""" SH1 = SignalHolder(self.test_signal) SH2 = SignalHolder(self.test_signal) data = { "is_diff": False, "sh1_len": 1, "sh2_len": 1, "sh1_not_in_sh2": SignalHolder(), "sh2_not_in_sh1": SignalHolder() } self.assertEqual(data, SH1.compare(SH2)) SH2 = SignalHolder(self.test_signal2) self.assertNotEqual(data, SH1.compare(SH2))
def test_register_0_strength_signal(self): """Attempts to register a signal w/ strength = 0.""" SH = SignalHolder() SH.register(self.test_signal_0_strength) self.assertEqual(0, len(SH))
def test_contains_tag(self): """Creates SH with a signal, checks 'contains' idiom for tags.""" SH = SignalHolder(self.test_signal) self.assertEqual(len(SH), 1) self.assertIn(self.test_signal.slug, SH)
def test_register_one_signal(self): """Creates empty SH, registers 1 signal, checks for presence.""" SH = SignalHolder() SH.register(self.test_signal) self.assertEqual(1, len(SH)) self._assert_same_signal(self.test_signal, SH[0])
def test_init_signal_list(self): """Creates SignalHolder with list of signals, checks for presence.""" SH = SignalHolder([self.test_signal, self.test_signal2]) self.assertEqual(2, len(SH)) self._assert_same_signal(self.test_signal, SH[0]) self._assert_same_signal(self.test_signal2, SH[1])
class BaseTestCase(unittest.TestCase): """Base class for building new tests :attribute str test_name: A name like ``XML_EXTERNAL_ENTITY_BODY``, containing the test type and the portion of the request template being tested :attribute list failures: A collection of "failures" raised by tests :attribute bool dead: Flip this if one of the requests doesn't return a response object :attribute client: HTTP client to be used by the test :attribute init_req: Initial request (loaded from request template) :attribute init_resp: Response to the initial request :attribute test_req: Request sent by the test for analysis :attribute test_resp: Response to the test request :attribute init_signals: Holder for signals on `init_req` :attribute test_signals: Holder for signals on `test_req` :attribute diff_signals: Holder for signals between `init_req` and `test_req` """ test_name = None failures = [] errors = [] dead = False client = client() init_req = None init_resp = None test_req = None test_resp = None init_signals = SignalHolder() test_signals = SignalHolder() diff_signals = SignalHolder() @classmethod def register_opts(cls): pass @classmethod def get_test_cases(cls, filename, file_content, meta_vars): """Returns tests for given TestCase class (overwritten by children).""" yield cls @classmethod def create_init_request(cls, filename, file_content, meta_vars): """Parses template and creates init request object This method does not send the initial request, instead, it only creates the object for use in the debug test :param str filename: name of template file :param str file_content: content of template file as string """ request_obj = parser.create_request(file_content, CONF.syntribos.endpoint, meta_vars) cls.init_req = request_obj cls.init_resp = None cls.init_signals = None cls.template_path = filename @classmethod def send_init_request(cls, filename, file_content, meta_vars): """Parses template, creates init request object, and sends init request This method sends the initial request, which is the request created after parsing the template file. This request will not be modified any further by the test cases themselves. :param str filename: name of template file :param str file_content: content of template file as string """ if not cls.init_req: cls.init_req = parser.create_request(file_content, CONF.syntribos.endpoint, meta_vars) prepared_copy = cls.init_req.get_prepared_copy() cls.prepared_init_req = prepared_copy cls.init_resp, cls.init_signals = cls.client.send_request( prepared_copy) if cls.init_resp is not None: # Get the computed body and add it to our RequestObject # TODO(cneill): Figure out a better way to handle this discrepancy cls.init_req.body = cls.init_resp.request.body else: cls.dead = True @classmethod def extend_class(cls, new_name, kwargs): """Creates an extension for the class Each TestCase class created is added to the `test_table`, which is then read in by the test runner as the master list of tests to be run. :param str new_name: Name of new class to be created :param dict kwargs: Keyword arguments to pass to the new class :rtype: class :returns: A TestCase class extending :class:`BaseTestCase` """ new_name = replace_invalid_characters(new_name) if not isinstance(kwargs, dict): raise Exception("kwargs must be a dictionary") new_cls = type(new_name, (cls, ), kwargs) new_cls.__module__ = cls.__module__ return new_cls @classmethod def tearDownClass(cls): super(BaseTestCase, cls).tearDownClass() if not cls.failures: if "EXCEPTION_RAISED" in cls.test_signals: sig = cls.test_signals.find(tags="EXCEPTION_RAISED")[0] exc_name = type(sig.data["exception"]).__name__ if ("CONNECTION_FAIL" in sig.tags): six.raise_from( FatalHTTPError( "The remote target has forcibly closed the connection " "with Syntribos and resulted in exception '{}'. This " "could potentially mean that a fatal error was " "encountered within the target application or server" " itself.".format(exc_name)), sig.data["exception"]) else: raise sig.data["exception"] @classmethod def tearDown(cls): get_slugs = [sig.slug for sig in cls.test_signals] get_checks = [sig.check_name for sig in cls.test_signals] test_signals_used = "Signals: " + str(get_slugs) LOG.debug(test_signals_used) test_checks_used = "Checks used: " + str(get_checks) LOG.debug(test_checks_used) def run_test_case(self): """This kicks off the test(s) for a given TestCase class After running the tests, an `AssertionError` is raised if any tests were added to self.failures. :raises: :exc:`AssertionError` """ if not self.dead: try: self.test_case() except Exception as e: self.errors += e raise if self.failures: raise AssertionError def test_case(self): """This method is overwritten by individual TestCase classes It represents the actual test that is called in :func:`run_test_case`, and handles populating `self.failures` """ pass def register_issue(self, defect_type, severity, confidence, description): """Adds an issue to the test's list of issues Creates a :class:`syntribos.issue.Issue` object, with given function parameters as instances variables, and registers the issue as a failure and associates the test's metadata to it. :param defect_type: The type of vulnerability that Syntribos believes it has found. This may be something like 500 error or DoS, regardless tof whathe Test Type is. :param severity: "Low", "Medium", or "High", depending on the defect :param description: Description of the defect :param confidence: The confidence of the defect :returns: new issue object with metadata associated :rtype: Issue """ issue = syntribos.Issue(defect_type=defect_type, severity=severity, confidence=confidence, description=description) issue.request = self.test_req if self.test_req else self.init_req issue.response = self.test_resp if self.test_resp else self.init_resp issue.template_path = self.template_path issue.parameter_location = self.parameter_location issue.test_type = self.test_name url_components = urlparse(self.init_resp.url) issue.target = url_components.netloc issue.path = url_components.path issue.init_signals = self.init_signals issue.test_signals = self.test_signals issue.diff_signals = self.diff_signals self.failures.append(issue) return issue
def test_init_one_signal(self): """Creates SignalHolder with 1 signal, checks for presence.""" SH = SignalHolder(self.test_signal) self.assertEqual(1, len(SH)) self._assert_same_signal(self.test_signal, SH[0])