예제 #1
0
 def test_report_result(self):
     port = random.randint(8000, 9000)
     cl = csclient.Client(port=port,
                          scheme='http',
                          version_file=self._test_version_file)
     wu = csclient.WorkUnit()
     wu.plugin = Testlib.TEST_PLUGIN_NAME
     cl._versions['plugin_versions'][wu.plugin] = 'test_pl_v-1234'
     try:
         fd, wu.test_file = tempfile.mkstemp()
         os.close(fd)
         with open(wu.test_file, 'wb') as fp:
             fp.write(os.urandom(32) * 1024)
         with Testlib.TestServer(port) as srv:
             srv.set_mode('report')
             response = cl.report_result(count=1,
                                         defect='BADf00D',
                                         failure='DEADBEEF',
                                         file_name=wu.test_file,
                                         log='',
                                         plugin=wu.plugin,
                                         name='test')
         self.assertTrue(response)
     finally:
         if os.path.isfile(wu.test_file):
             os.remove(wu.test_file)
    def report_work(self, wu):
        """
        report_work in this example will populate the WorkUnit with dummy data
        and report the work back to the CrashStash server.
        """
        # populate the dummy work unit
        wu.duration = wu.duration if wu.duration else random.randint(1, 1200)
        wu.iterations = wu.iterations if wu.iterations else random.randint(
            1, 40000)

        log.info('Reporting WorkUnit...')
        log.info('Plugin: %s', wu.plugin)
        log.info('Duration: %d', wu.duration)
        log.info('Iterations: %d', wu.iterations)
        log.info('Contacting server with work report...')

        c = csclient.Client(addr=self._ip_addr,
                            cert=self._cert,
                            port=self._port,
                            scheme=self._scheme,
                            debug=True)
        # attempt to report work unit to server
        if c.report_work(wu):
            log.info('Work reported')
        else:
            # TODO: a retry mechanism should be added
            sc = c.get_status_code()
            if sc is not None and sc != 200:
                log.warn('Status code: %d', sc)
            log.warn('Failed to report work unit')
예제 #3
0
 def test_request_bad_url(self):
     port = random.randint(8000, 9000)
     cl = csclient.Client(port=port,
                          scheme='http',
                          version_file=self._test_version_file)
     with Testlib.TestServer(port):
         wu = cl.request('csclient/bad_url1234')
     self.assertIsNone(wu)
예제 #4
0
 def test_request_server_hang(self):
     port = random.randint(8000, 9000)
     cl = csclient.Client(port=port,
                          scheme='http',
                          timeout=0.1,
                          version_file=self._test_version_file)
     with Testlib.TestServer(port) as srv:
         srv.set_mode('server_hang')
         wu = cl.request()
     self.assertIsNone(wu)
    def request(self, plugin_name=None):
        """
        Makes a request to the CrashStash server for a work unit.

        plugin_name is used to request work for a specific plugin. If None
        is used work for any active plugin can be assigned.

        Returns a WorkUnit or None if an update for the client or a plugin was
        received instead of a WorkUnit. None is also returned if there is a
        problem communicating with the server. None typically means try again.
        """
        c = csclient.Client(addr=self._ip_addr,
                            cert=self._cert,
                            port=self._port,
                            scheme=self._scheme,
                            debug=True)
        log.info('The current client version is: %s', c.get_client_version())
        log.info('Contacting server with work request...')
        if plugin_name is not None:
            log.info('Plugin manually requested: %s...', plugin_name)
        wu = c.request(plugin=plugin_name)
        sc = c.get_status_code()
        if sc is not None and sc != 200:
            log.warn('Status code: %d', sc)
        if c.required_client_update():
            # a client update is available, unpack it and return None
            # so another work request is made.
            log.info('Looks like the client needs to be updated.')
            new_version, file_name = c.required_client_update()
            self._fake_client_update(new_version, file_name)
            return None
        if c.required_plugin_update():
            # a plugin update is available, unpack it and return None
            # so another work request is made.
            pl_name, pl_version, pl_file = c.required_plugin_update()
            log.info('Looks like there is an update for %s.', pl_name)
            self._fake_plugin_update(pl_name, pl_version, pl_file)
            return None
        if not type(wu) == type(csclient.WorkUnit()):
            log.warn('Something went wrong expected a WorkUnit.')
            log.warn('Has this client been approved?')
            # wait to avoid hammering the server
            time.sleep(5)
            return None
        log.info('Received a work unit')
        log.info('plugin: %s', wu.plugin)
        log.info('for %d seconds or %d iterations (0 == no limit)',
                 wu.duration, wu.iterations)
        log.info('use this test case: %s', wu.test_file)
        log.info('it should have this sha1 hash: %s', wu.test_hash)
        if wu.allow_fuzzing:
            log.info('it is %sfuzzable', '' if wu.allow_fuzzing else 'NOT ')
        os.remove(wu.test_file)
        return wu
예제 #6
0
 def test_request_client_update(self):
     port = random.randint(8000, 9000)
     cl = csclient.Client(port=port,
                          scheme='http',
                          version_file=self._test_version_file)
     with Testlib.TestServer(port) as srv:
         srv.set_mode('client_update')
         wu = cl.request()
     self.assertIsNone(wu)
     self.assertEqual(cl.get_client_version(), '')
     self.assertTrue(os.path.isfile(cl._client_update))
     os.remove(cl._client_update)
     self.assertEqual(cl._versions['client_pending_update'],
                      '20140918_110500')
예제 #7
0
 def test_report_work_unit(self):
     port = random.randint(8000, 9000)
     cl = csclient.Client(port=port,
                          scheme='http',
                          version_file=self._test_version_file)
     wu = csclient.WorkUnit()
     wu.plugin = Testlib.TEST_PLUGIN_NAME
     wu.duration = 1200
     wu.iterations = 4567
     cl._versions['plugin_versions'][wu.plugin] = 'test_pl_v-1234'
     with Testlib.TestServer(port) as srv:
         srv.set_mode('report')
         response = cl.report_work(wu)
     self.assertTrue(response)
예제 #8
0
 def test_request_fuzz(self):
     for _ in range(10):  # increase this to 1000 for a longer run
         port = random.randint(6000, 9000)
         cl = csclient.Client(port=port,
                              scheme='http',
                              version_file=self._test_version_file)
         with Testlib.TestServer(port) as srv:
             srv.set_mode('fuzz')
             wu = cl.request()
         if wu and os.path.isfile(wu.test_file):
             os.remove(wu.test_file)
     if os.path.isfile(cl._plugin_update):
         os.remove(cl._plugin_update)
     if os.path.isfile(cl._client_update):
         os.remove(cl._client_update)
    def report_result(self, wu):
        """
        report_result in this example will create a dummy result and report
        it back to the CrashStash server.
        """
        log.info('Generating a result')

        # generate random dummy test case
        fd, test_file = tempfile.mkstemp()
        os.close(fd)
        with open(test_file, 'wb') as fp:
            fp.write(os.urandom(2**random.randint(0, 20)))
        try:
            # generate random dummy result
            result = {
                'classification':
                random.choice(['UNKNOWN', 'EXPLOITABLE', 'TIMEOUT']),
                'count':
                random.randint(1, 10),
                'defect':
                hashlib.sha1(os.urandom(random.randint(0, 1))).hexdigest(),
                'failure':
                hashlib.sha1(os.urandom(random.randint(1, 3))).hexdigest(),
                'file_name':
                test_file,
                'log':
                'stuff happened...\nstack()\ntrace()\nfoo()\n',
                'name':
                'test_file.bin',
                'plugin':
                wu.plugin
            }
            c = csclient.Client(addr=self._ip_addr,
                                cert=self._cert,
                                port=self._port,
                                scheme=self._scheme,
                                debug=True)
            log.info('Reporting Result...')
            if c.report_result(**result):
                log.info('Result reported')
            else:
                # TODO: a retry mechanism should be added
                sc = c.get_status_code()
                if sc is not None and sc != 200:
                    log.warn('Status code: %d', sc)
                log.warn('Failed to report result')
        finally:
            os.remove(test_file)
예제 #10
0
 def test_request(self):
     port = random.randint(8000, 9000)
     cl = csclient.Client(port=port,
                          scheme='http',
                          version_file=self._test_version_file)
     with Testlib.TestServer(port) as srv:
         srv.set_mode('request')
         wu = cl.request()
     self.assertIsNone(cl.required_client_update())
     self.assertIsNone(cl.required_plugin_update())
     self.assertIsNotNone(wu)
     self.assertEqual(wu.allow_fuzzing, True)
     self.assertEqual(wu.duration, 1200)
     self.assertEqual(wu.iterations, 0)
     self.assertEqual(wu.plugin, Testlib.TEST_PLUGIN_NAME)
     self.assertEqual(wu.test_name, 'test_case_fn.bin')
     self.assertTrue(os.path.isfile(wu.test_file))
     with open(wu.test_file, 'rb') as fp:
         file_hash = hashlib.sha1(fp.read()).hexdigest()
     os.remove(wu.test_file)
     self.assertEqual(wu.test_hash, file_hash)
예제 #11
0
    def test_report_work_unit_fuzz(self):
        for _ in range(10):  # increase this to 1000 for a longer test
            port = random.randint(6000, 9000)
            cl = csclient.Client(port=port,
                                 scheme='http',
                                 timeout=random.choice([0.001, 0.01])
                                 if Testlib.Fuzz.dice(20) else None,
                                 version_file=self._test_version_file)
            wu = csclient.WorkUnit()
            if Testlib.Fuzz.dice():
                wu.allow_fuzzing = Testlib.Fuzz.string(None)
            elif not Testlib.Fuzz.dice():
                wu.allow_fuzzing = random.choice(['true', ''])

            if Testlib.Fuzz.dice():
                wu.duration = Testlib.Fuzz.int()
            elif not Testlib.Fuzz.dice():
                wu.duration = 1200

            if Testlib.Fuzz.dice():
                wu.iterations = Testlib.Fuzz.int()
            elif not Testlib.Fuzz.dice():
                wu.iterations = 1200

            if Testlib.Fuzz.dice():
                wu.plugin = Testlib.Fuzz.string(None)
            elif not Testlib.Fuzz.dice():
                wu.plugin = Testlib.TEST_PLUGIN_NAME
                cl._versions['plugin_versions'][wu.plugin] = 'test_pl_v-1234'

            if Testlib.Fuzz.dice():
                wu.test_name = Testlib.Fuzz.string(None)
            elif not Testlib.Fuzz.dice():
                wu.test_name = 'test_name'

            if Testlib.Fuzz.dice(20):
                wu = None
            with Testlib.TestServer(port) as srv:
                srv.set_mode('report')
                response = cl.report_work(wu)
예제 #12
0
 def test_request_project_update(self):
     port = random.randint(8000, 9000)
     cl = csclient.Client(port=port,
                          scheme='http',
                          version_file=self._test_version_file)
     with Testlib.TestServer(port) as srv:
         srv.set_mode('plugin_update')
         wu = cl.request()
     self.assertIsNone(wu)
     self.assertTrue(os.path.isfile(cl._plugin_update))
     self.assertEqual(cl.get_plugin_version(Testlib.TEST_PLUGIN_NAME), '')
     self.assertEqual(
         cl.required_plugin_update(),
         (Testlib.TEST_PLUGIN_NAME, '20140918_110500', cl._plugin_update))
     self.assertEqual(cl._versions['plugin_pending_update'],
                      (Testlib.TEST_PLUGIN_NAME, '20140918_110500'))
     cl.update_plugin()
     self.assertIsNone(cl.required_plugin_update())
     self.assertEqual(cl.get_plugin_version(Testlib.TEST_PLUGIN_NAME),
                      '20140918_110500')
     self.assertTrue(os.path.isdir(Testlib.TEST_PLUGIN_NAME))
     shutil.rmtree(Testlib.TEST_PLUGIN_NAME)
예제 #13
0
    def run(self):
        """
        This is the main loop. Operations managed here include:
            - communication with the server
            - requesting work
            - reporting work and results
            - client and plugin updates
            - preparation to perform work
            - post work cleanup
        """
        log.info('Ctrl+C to quit')
        try:
            base_dir = os.getcwd()
            done = False

            while not done:
                log.info('Current time: %s',
                         time.strftime('%Y/%m/%d %H:%M:%S'))

                if self._scheme != 'https':
                    log.warning('Not using HTTPS')
                elif self._cert is None:
                    log.warning('Server certificate NOT provided')
                    log.warning('Server verification will NOT be performed')

                conn = csclient.Client(addr=self._ip_addr,
                                       cert=self._cert,
                                       port=self._port,
                                       scheme=self._scheme,
                                       version_file=self._version_file)

                log.info('Client version: %s', conn.get_client_version())
                log.info('Contacting %s with work request...', self._ip_addr)
                if self._requested_pl is not None:
                    log.info('Plugin manually requested: %s',
                             self._requested_pl)
                w_unit = conn.request(plugin=self._requested_pl)
                sc = conn.get_status_code()

                if sc is not None and sc != 200:
                    log.warning('Status code: %d', sc)
                    log.warning('Has this client been approved?')
                    if self._test_mode:
                        return False
                    log.warning('Waiting %d seconds', self._retry_delay)
                    time.sleep(self._retry_delay)
                    continue

                if conn.required_client_update():
                    cl_version, update_file = conn.required_client_update()
                    log.info('Client requires update to version: %s',
                             cl_version)
                    self._unpack_archive(update_file)
                    conn.update_client_version(cl_version)
                    return True

                if conn.required_plugin_update():
                    pl_name = conn.required_plugin_update()[0]
                    log.info('%s will now be updated', pl_name)
                    conn.update_plugin(dest_path='projects')
                    continue

                if not type(w_unit) == type(csclient.WorkUnit()):
                    log.warning('Server transaction failed')
                    log.warning('Waiting %d seconds', self._retry_delay)
                    time.sleep(self._retry_delay)
                    continue

                log.info('Got work for %s', w_unit.plugin)
                working_dir = '%s_%s' % (time.strftime('%Y%m%d-%H-%M-%S'),
                                         w_unit.plugin)
                os.mkdir(working_dir)

                try:
                    self._perform_work(w_unit, working_dir)
                except KeyboardInterrupt:
                    done = True
                    log.info('User interrupted work')
                    log.info('Will exit following work report')
                log.info('Reporting work')

                while not conn.report_work(self._report_unit):
                    log.warning('Failed to report work waiting %d seconds',
                                self._retry_delay)
                    time.sleep(self._retry_delay)

                log.info('%d result(s) to report', len(self._results))
                f_ids = list(self._results.keys())

                while f_ids:
                    if conn.report_result(**self._results[f_ids[0]]):
                        log.info('Result reported')
                        f_ids.pop(0)
                    else:
                        log.warning(
                            'Failed to report result waiting %d seconds',
                            self._retry_delay)
                        time.sleep(self._retry_delay)

                log.info('Work report complete')
                os.chdir(base_dir)
                self._report_unit = None
                self._results = None
                alf.delete(w_unit.test_file)
                alf.delete(os.path.abspath(working_dir))
                self._do_deletes()

        except KeyboardInterrupt:
            log.info('User interrupted')
            done = True

        finally:
            pass  #TODO: report errors

        return not done  # indicate relaunch should be performed
예제 #14
0
 def test_request_no_server(self):
     cl = csclient.Client(port=random.randint(8000, 9000),
                          scheme='http',
                          version_file=self._test_version_file)
     wu = cl.request()
     self.assertIsNone(wu)
예제 #15
0
    def test_report_result_fuzz(self):
        for _ in range(10):  # increase this to 1000 for a longer test
            port = random.randint(6000, 9000)
            cl = csclient.Client(port=port,
                                 scheme='http',
                                 timeout=random.choice([0.001, 0.01])
                                 if Testlib.Fuzz.dice(20) else None,
                                 version_file=self._test_version_file)
            wu = csclient.WorkUnit()
            args = {}
            if Testlib.Fuzz.dice():
                args['classification'] = Testlib.Fuzz.string(None)
            else:
                args['classification'] = 'UNKNOWN'

            if Testlib.Fuzz.dice():
                args['count'] = Testlib.Fuzz.int()
            else:
                args['count'] = 1

            if Testlib.Fuzz.dice():
                args['defect'] = Testlib.Fuzz.string(None)
            else:
                args['defect'] = 'BADf00D'

            if Testlib.Fuzz.dice():
                args['failure'] = Testlib.Fuzz.string(None)
            else:
                args['failure'] = 'DEADBEEF'

            if Testlib.Fuzz.dice():
                args['log'] = Testlib.Fuzz.string(None)
            else:
                args['log'] = 'test'

            if Testlib.Fuzz.dice():
                wu.plugin = Testlib.Fuzz.string(None)
            else:
                wu.plugin = Testlib.TEST_PLUGIN_NAME
                cl._versions['plugin_versions'][wu.plugin] = 'test_pl_v-1234'

            if Testlib.Fuzz.dice():
                wu.test_name = Testlib.Fuzz.string(None)
            else:
                wu.test_name = 'test_name'

            try:
                fd, wu.test_file = tempfile.mkstemp()
                os.close(fd)
                with open(wu.test_file, 'wb') as fp:
                    fp.write(os.urandom(random.choice([0, 1, 32])))
                with Testlib.TestServer(port) as srv:
                    srv.set_mode('report')
                    cl.report_result(
                        count=args['count'],
                        defect=args['defect'],
                        failure=args['failure'],
                        file_name=wu.test_file if not Testlib.Fuzz.dice() else
                        Testlib.Fuzz.string(None),
                        log=args['log'],
                        plugin=wu.plugin,
                        name=wu.test_name)
            finally:
                if os.path.isfile(wu.test_file):
                    os.remove(wu.test_file)